├── .gitignore ├── README.md ├── anki ├── 00_tooling.apkg ├── 01_Intro.apkg ├── 02_python.apkg ├── 03_00_pandas_basic.apkg ├── 03_01_pandas_columns.apkg ├── 03_02_pandas_functions.apkg ├── 03_03_pandas_filtering.apkg ├── 03_04_pandas_granularity.apkg ├── 03_05_pandas_combine.apkg ├── 04_sql.apkg ├── 05_scraping.apkg ├── 06_stats.apkg └── 07_modeling.apkg ├── code ├── 02_python.py ├── 03_00_basics.py ├── 03_01_columns.py ├── 03_02_functions.py ├── 03_03_filter.py ├── 03_04_granularity.py ├── 03_05_combine.py ├── 04_sql.py ├── 05_01_scraping.py ├── 05_02_aba.py ├── 05_03_api.py ├── 05_04_wrapper.py ├── 06_01_summary.py ├── 06_02_shot_chart.py ├── 07_01_ols.py ├── 07_02_coinflip.py ├── 07_03_ols2.py ├── 07_04_logit.py └── 07_05_random_forest.py ├── data ├── basketball-data.sqlite ├── box_adv.csv ├── games.csv ├── json │ ├── players.csv │ ├── players.json │ ├── teams.csv │ └── teams.json ├── nba_court.jpg ├── player_game.csv ├── players.csv ├── problems │ ├── combine1 │ │ ├── def.csv │ │ ├── pts.csv │ │ └── reb.csv │ └── combine2 │ │ ├── center.csv │ │ ├── forward.csv │ │ └── guard.csv ├── shot.csv ├── shots.csv ├── team_games.csv └── teams.csv └── solutions-to-exercises ├── 02_python_answers.py ├── 03_pandas_answers.py ├── 04_sql_answers.py ├── 05_api_answers.py ├── 05_scraping_answers.py ├── 06_plotting_answers.py ├── 07_modeling_answers.py ├── 6-1a.png ├── 6-1b.png ├── 6-1c.png ├── 6-1d.png ├── 6-1e.png ├── 6-2.png ├── 6-2a.png └── 6-2b.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | make_tables.py 3 | misc-book 4 | data/dft_East.csv 5 | data/dft_West.csv 6 | data/game_simple.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | These are the files for the book [Learn to Code with Basketball](https://codebasketball.com). 3 | 4 | If you're not familiar with Git or GitHub, no problem. Just click the `Source 5 | code` link under the latest release to download the files. This will download 6 | a file called `code-basketball-files-vX.X.X.zip`, where X.X.X is the latest 7 | version. 8 | 9 | When you unzip these (note in the book I've dropped the version number and 10 | renamed the directory just `code-basketball-files`, which you can do too) 11 | you'll see four sub-directories: `code`, `data`, `anki`, 12 | `solutions-to-excercises`. 13 | 14 | You don't have to do anything with these right now except know where you put 15 | them. For example, on my mac, I have them in my home directory: 16 | 17 | `/Users/nathanbraun/code-basketball-files` 18 | 19 | If I were using Windows, it might look like this: 20 | 21 | `C:\Users\nathanbraun\code-basketball-files` 22 | 23 | Set these aside for now and we'll pick them up in chapter 2. 24 | 25 | ## Changelog 26 | ### v0.5.2 (2025-02-28) 27 | Added Spyder instructions for Intel Macs. 28 | 29 | ### v0.5.0 (2024-12-06) 30 | Updated instructions for installing Python (now recc Spyder directly vs via 31 | Anaconda). Also cover a few other options. 32 | 33 | Minor typos and fixes. 34 | 35 | ### v0.4.0 (2024-06-18) 36 | Updated the seaborn chapter to avoid misc warnings. 37 | 38 | ### v0.3.0 (2024-05-01) 39 | Updated the API chapter again since to reflect fact [Ball Don't 40 | Lie](https://www.balldontlie.io/#introduction) now requires making a free 41 | account (it's easy to do). 42 | 43 | ### v0.2.0 (2024-02-07) 44 | Updated the API chapter since the old API stopped working. Now using [Ball 45 | Don't Lie](https://www.balldontlie.io/#introduction). Fixed some typos (thanks 46 | Alex!) 47 | 48 | ### v0.1.4 (2023-12-01) 49 | Fixed some typos and updates (thanks Alex!) 50 | 51 | ### v0.1.3 (2023-11-15) 52 | Fixed some typos. 53 | 54 | ### v0.1.1 (2023-09-21) 55 | Minor clarify fix to exercise 3.0.x (thanks Terence!) 56 | 57 | ### v0.1.0 (2023-04-21) 58 | Heard from a reader (thanks Arnold!) that some of the nbasense API endpoints no 59 | longer worked, and updated the book to use only the ones that do. Doesn't 60 | impact things too much since this was mainly for demonstration purposes. 61 | 62 | ### v0.0.4 (2022-12-16) 63 | Fixed a few typos (thanks Victor!) 64 | 65 | ### v0.0.3 (2022-12-02) 66 | Minor rewording. 67 | 68 | ### v0.0.2 (2022-07-27) 69 | Fix minor typo. 70 | 71 | ### v0.0.1 (2022-07-22) 72 | Adding this README! 73 | -------------------------------------------------------------------------------- /anki/00_tooling.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/00_tooling.apkg -------------------------------------------------------------------------------- /anki/01_Intro.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/01_Intro.apkg -------------------------------------------------------------------------------- /anki/02_python.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/02_python.apkg -------------------------------------------------------------------------------- /anki/03_00_pandas_basic.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_00_pandas_basic.apkg -------------------------------------------------------------------------------- /anki/03_01_pandas_columns.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_01_pandas_columns.apkg -------------------------------------------------------------------------------- /anki/03_02_pandas_functions.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_02_pandas_functions.apkg -------------------------------------------------------------------------------- /anki/03_03_pandas_filtering.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_03_pandas_filtering.apkg -------------------------------------------------------------------------------- /anki/03_04_pandas_granularity.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_04_pandas_granularity.apkg -------------------------------------------------------------------------------- /anki/03_05_pandas_combine.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/03_05_pandas_combine.apkg -------------------------------------------------------------------------------- /anki/04_sql.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/04_sql.apkg -------------------------------------------------------------------------------- /anki/05_scraping.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/05_scraping.apkg -------------------------------------------------------------------------------- /anki/06_stats.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/06_stats.apkg -------------------------------------------------------------------------------- /anki/07_modeling.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/anki/07_modeling.apkg -------------------------------------------------------------------------------- /code/02_python.py: -------------------------------------------------------------------------------- 1 | ############## 2 | # basic python 3 | # v0.0.1 4 | ############## 5 | 6 | ########################## 7 | # how to read this chapter 8 | ########################## 9 | 1 + 1 10 | 11 | ########## 12 | # comments 13 | ########## 14 | 15 | # print the result of 1 + 1 16 | print(1 + 1) 17 | 18 | ########### 19 | # variables 20 | ########### 21 | 22 | three_pt_made = 4 23 | 24 | three_pt_made 25 | 3*three_pt_made 26 | 27 | three_pt_made = three_pt_made + 1 28 | 29 | three_pt_made 30 | 31 | #################### 32 | # types of variables 33 | #################### 34 | 35 | over_under = 216 # int 36 | fg_percentage = 0.48 # float 37 | 38 | starting_c = 'Karl-Anthony Towns' 39 | starting_pg = "D'Angelo Russell" 40 | 41 | type(starting_c) 42 | 43 | type(over_under) 44 | 45 | starters = f'{starting_c}, {starting_pg}, etc.' 46 | starters 47 | 48 | # string methods 49 | 'from downtown!'.upper() 50 | 51 | 'Ron Artest'.replace('Artest', 'World Peace') 52 | 53 | #################################### 54 | # How to figure things out in Python 55 | #################################### 56 | 'lebron james'.capitalize() 57 | 58 | ' lebron james' 59 | 'lebron james' 60 | 61 | ####### 62 | # bools 63 | ####### 64 | team1_pts = 110 65 | team2_pts = 120 66 | 67 | # and these are all bools: 68 | team1_won = team1_pts > team2_pts 69 | team2_won = team1_pts < team2_pts 70 | teams_tied = team1_pts == team2_pts 71 | teams_did_not_tie = team1_pts != team2_pts 72 | 73 | type(team1_won) 74 | teams_did_not_tie 75 | 76 | # error because test for equality is ==, not = 77 | # teams_tied = (team1_pts = team2_pts) # commented out since it throws an error 78 | 79 | shootout = (team1_pts > 130) and (team2_pts > 130) 80 | at_least_one_good_team = (team1_pts > 120) or (team2_pts > 120) 81 | you_guys_are_bad = not ((team1_pts > 100) or (team2_pts > 100)) 82 | meh = not (shootout or at_least_one_good_team or you_guys_are_bad) 83 | 84 | ############### 85 | # if statements 86 | ############### 87 | if team1_won: 88 | message = "Nice job team 1!" 89 | elif team2_won: 90 | message = "Way to go team 2!!" 91 | else: 92 | message = "must have tied!" 93 | 94 | message 95 | 96 | ################# 97 | # container types 98 | ################# 99 | 100 | # lists 101 | roster_list = ['kevin durant', 'kyrie irving', 'james harden'] 102 | 103 | roster_list[0] 104 | roster_list[0:2] 105 | roster_list[-2:] 106 | 107 | # dicts 108 | roster_dict = {'PF': 'kevin durant', 109 | 'SG': 'kyrie irving', 110 | 'PG': 'james harden'} 111 | 112 | roster_dict['PF'] 113 | roster_dict['C'] = 'deandre jordan' 114 | 115 | pos = 'PF' 116 | roster_dict[pos] 117 | 118 | # unpacking 119 | sg, pg = ['kyrie irving', 'james harden'] 120 | 121 | sg = 'kyrie irving' 122 | pg = 'james harden' 123 | 124 | # gives an error - n of variables doesn't match n items in list 125 | # sg, pg = ['kevin durant', 'kyrie irving', 'james harden'] # commented out w/ error 126 | 127 | ####### 128 | # loops 129 | ####### 130 | 131 | # looping over a list 132 | roster_list = ['kevin durant', 'james harden', 'kyrie irving'] 133 | 134 | roster_list_upper = ['', '', ''] 135 | i = 0 136 | for player in roster_list: 137 | roster_list_upper[i] = player.title() 138 | i = i + 1 139 | 140 | roster_list_upper 141 | 142 | for x in roster_dict: 143 | print(f"position: {x}") 144 | 145 | for x in roster_dict: 146 | print(f"position: {x}") 147 | print(f"player: {roster_dict[x]}") 148 | 149 | for x, y in roster_dict.items(): 150 | print(f"position: {x}") 151 | print(f"player: {y}") 152 | 153 | ################ 154 | # comprehensions 155 | ################ 156 | 157 | # lists 158 | roster_list 159 | roster_list_proper = [x.title() for x in roster_list] 160 | roster_list_proper 161 | 162 | roster_list_proper_alt = [y.title() for y in roster_list] 163 | 164 | type([x.title() for x in roster_list]) 165 | [x.title() for x in roster_list][:2] 166 | 167 | roster_last_names = [full_name.split(' ')[1] for full_name in roster_list] 168 | roster_last_names 169 | 170 | full_name = 'kevin durant' 171 | full_name.split(' ') 172 | full_name.split(' ')[1] 173 | 174 | roster_k_only = [ 175 | x for x in roster_list if x.startswith('k')] 176 | roster_k_only 177 | 178 | 'kevin durant'.startswith('k') 179 | 180 | 'james harden'.startswith('k') 181 | 182 | 'kyrie irving'.startswith('k') 183 | 184 | roster_k_only_title = [ 185 | x.title() for x in roster_list if x.startswith('k')] 186 | roster_k_only_title 187 | 188 | # dicts 189 | salary_per_player = { 190 | 'kevin durant': 39058950, 'kyrie irving': 33460350, 'james harden': 41254920} 191 | 192 | salary_m_per_upper_player = { 193 | name.upper(): salary/1000000 for name, salary in salary_per_player.items()} 194 | 195 | salary_m_per_upper_player 196 | 197 | sum([1, 2, 3]) 198 | 199 | sum([salary for _, salary in salary_per_player.items()]) 200 | 201 | ########### 202 | # functions 203 | ########### 204 | len(['kevin durant', 'james harden', 'kyrie irving']) 205 | 206 | pts_per_3 = len(['kevin durant', 'james harden', 'kyrie irving']) 207 | pts_per_3 208 | 209 | 4 + len(['kevin durant', 'james harden', 'kyrie irving']) 210 | 211 | def pts(fg2, fg3, ft): 212 | """ 213 | multi line strings in python are between three double quotes 214 | 215 | it's not required, but the convention is to put what the fn does in one of these multi line strings (called "docstring") right away in function 216 | 217 | this function takes number of 2 point fgs, 3 point fgs, and free throws and 218 | returns total points scored 219 | """ 220 | return fg2*2 + fg3*3 + ft*1 221 | 222 | 223 | # this gives an error: fg2 is only defined inside pts 224 | # print(fg2) 225 | 226 | def pts_noisy(fg2, fg3, ft): 227 | """ 228 | this function takes number of 2 point fgs, 3 point fgs, and free throws and 229 | returns total points scored 230 | 231 | it also prints out fg2 232 | """ 233 | print(fg2) # works here since we're inside fn 234 | return fg2*2 + fg3*3 + ft*1 235 | 236 | pts_noisy(8, 4, 5) 237 | 238 | # side effects 239 | def is_player_on_team(player, team): 240 | """ 241 | take a player string and team list and check whether the player is on team 242 | 243 | do this by adding the player to the team, then returning True if the player shows up 2 or more times 244 | """ 245 | team.append(player) 246 | return team.count(player) >= 2 247 | 248 | roster_list = ['kevin durant', 'james harden', 'kyrie irving'] 249 | is_player_on_team('jared dudley', roster_list) 250 | 251 | roster_list 252 | is_player_on_team('jared dudley', roster_list) 253 | 254 | roster_list 255 | 256 | # function arguments 257 | ## Positional vs Keyword Arguments 258 | 259 | pts(8, 4, 5) 260 | pts(8, 5, 4) # order matters! 261 | 262 | pts? 263 | 264 | pts(fg3=4, fg2=8, ft=5) # keyword arguments 265 | pts(8, 4, ft=5) 266 | 267 | # error: keyword arguments can't come before positional arguments 268 | # pts(ft=5, 8, 4) 269 | 270 | ## Default Values for Arguments 271 | 272 | # error: leaving off a an argument 273 | # pts(4, 2) 274 | 275 | def pts_w_default(fg2, fg3, ft=0): 276 | """ 277 | this function takes number of 2 point fgs, 3 point fgs, and free throws and 278 | returns total points scored 279 | """ 280 | return fg2*2 + fg3*3 + ft*1 281 | 282 | pts_w_default(8, 4) 283 | 284 | # error: leaving out required argument 285 | # pts_w_default(8) 286 | 287 | # error: required arguments need to come first 288 | # def pts_w_default_wrong(fg2=0, fg3, ft=0): 289 | # """ 290 | # this function takes number of 2 point fgs, 3 point fgs, and free throws and 291 | # returns total points scored 292 | # """ 293 | # return fg2*2 + fg3*3 + ft*1 294 | 295 | ##################################### 296 | # functions that take other functions 297 | ##################################### 298 | 299 | def do_to_list(working_list, working_fn, desc): 300 | """ 301 | this function takes a list, a function that works on a list, and a 302 | description 303 | 304 | it applies the function to the list, then returns the result along with 305 | description as a string 306 | """ 307 | 308 | value = working_fn(working_list) 309 | 310 | return f'{desc} {value}' 311 | 312 | def last_elem_in_list(working_list): 313 | """ 314 | returns the last element of a list. 315 | """ 316 | return working_list[-1] 317 | 318 | positions = ['C', 'PF', 'SF', 'SG', 'PG'] 319 | 320 | do_to_list(positions, last_elem_in_list, "last element in your list:") 321 | do_to_list([1, 2, 4, 8], last_elem_in_list, "last element in your list:") 322 | 323 | do_to_list(positions, len, "length of your list:") 324 | 325 | do_to_list([2, 3, 7, 1.3, 5], lambda x: 3*x[0], "first element in your list times 3 is:") 326 | 327 | # normally imports like this would be at the top of the file 328 | import os 329 | 330 | os.cpu_count() 331 | 332 | from os import path 333 | 334 | # change this to the location of your data 335 | DATA_DIR = '/Users/nathan/nba-book/data' 336 | path.join(DATA_DIR, 'shot.csv') 337 | os.path.join(DATA_DIR, 'shot.csv') # alt if we didn't want to import path 338 | -------------------------------------------------------------------------------- /code/03_00_basics.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | import pandas as pd 3 | 4 | # change this to the directory where the csv files that come with the book are 5 | # stored 6 | # on Windows it might be something like 'C:/mydir' 7 | 8 | DATA_DIR = './data' 9 | 10 | ############## 11 | # Loading data 12 | ############## 13 | shots = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 14 | 15 | type(shots) 16 | 17 | ################################## 18 | # DataFrame methods and attributes 19 | ################################## 20 | shots.head() 21 | 22 | shots.columns 23 | 24 | shots.shape 25 | 26 | ################################# 27 | # Working with subsets of columns 28 | ################################# 29 | # A single column 30 | shots['name'].head() 31 | 32 | type(shots['name']) 33 | 34 | shots['name'].to_frame().head() 35 | type(shots['name'].to_frame().head()) 36 | 37 | # Multiple columns 38 | shots[['name', 'dist', 'value', 'made']].head() 39 | 40 | type(shots[['name', 'dist', 'value', 'made']].head()) 41 | 42 | # commented out because it throws an error 43 | # shots['name', 'dist', 'value', 'made'].head() 44 | 45 | ########## 46 | # Indexing 47 | ########## 48 | shots[['name', 'dist', 'value', 'made']].head() 49 | 50 | shots.set_index('shot_id').head() 51 | 52 | # Copies and the inplace argument 53 | shots.head() # note: player_id not the index, even though we just set it 54 | 55 | shots.set_index('shot_id', inplace=True) 56 | shots.head() # now player_id is index 57 | 58 | # alternate to using inplace, reassign adp 59 | # reload shots with default 0, 1, ... index 60 | shots = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 61 | shots = shots.set_index('shot_id') 62 | shots.head() # now shot_id is index 63 | 64 | shots.reset_index().head() 65 | 66 | ############################# 67 | # Indexes keep things aligned 68 | ############################# 69 | shots_ot = shots.loc[shots['period'] > 4, ['name', 'dist', 'value']] 70 | shots_ot.head() 71 | 72 | shots_ot.sort_values('name', inplace=True) 73 | shots_ot.head() 74 | 75 | # assigning a new column 76 | shots_ot['made'] = shots['made'] 77 | shots_ot.head() 78 | 79 | # has the same index as shots['made'] and shots['made'] 80 | shots['made'].head() 81 | 82 | ################# 83 | # Outputting data 84 | ################# 85 | shots_ot.to_csv(path.join(DATA_DIR, 'shots_ot.csv')) 86 | 87 | shots_ot.to_csv(path.join(DATA_DIR, 'shots_ot_no_index.csv'), index=False) 88 | 89 | -------------------------------------------------------------------------------- /code/03_01_columns.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from os import path 3 | 4 | # change this to the directory where the csv files that come with the book are 5 | # stored 6 | # on Windows it might be something like 'C:/mydir' 7 | 8 | DATA_DIR = './data' 9 | 10 | # load player-game data 11 | pg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 12 | 13 | # book picks up here: 14 | 15 | # creating and modifying columns 16 | pg['pts_per_shot'] = 2 17 | pg[['game_id', 'player_id', 'pts_per_shot']].head() 18 | 19 | pg['pts_per_shot'] = 3 20 | pg[['game_id', 'player_id', 'pts_per_shot']].head() 21 | 22 | # math and number columns 23 | pg['pts_from_fgs'] = (pg['fg3m']*3 + (pg['fgm'] - pg['fg3m'])*2) 24 | pg[['name', 'game_id', 'pts_from_fgs']].head() 25 | 26 | import numpy as np # note: normally you'd import this at the top of the file 27 | 28 | pg['biggest_impact'] = np.abs(pg['plus_minus']) 29 | 30 | pg['ln_pts'] = np.log(pg['pts']) 31 | 32 | pg['court_length'] = 94 33 | 34 | pg[['name', 'game_id', 'court_length']].sample(5) 35 | 36 | # string columns 37 | pg['name'].str.upper().sample(5) 38 | 39 | pg['name'].str.replace('.', ' ').sample(5) 40 | 41 | (pg['name'] + ', ' + pg['team']).sample(5) 42 | 43 | pg['name'].str.replace('.', ' ').str.lower().sample(5) 44 | 45 | # boolean columns 46 | pg['is_a_guard'] = (pg['pos'] == 'Guard') 47 | pg[['name', 'is_a_guard']].sample(5) 48 | 49 | pg['is_a_forward_or_center'] = (pg['pos'] == 'Forward') | (pg['pos'] == 'Center') 50 | pg['good_guard_game'] = (pg['pos'] == 'Guard') & (pg['pts'] >= 30) 51 | pg['not_gt_10_pts_or_assists'] = ~((pg['pts'] > 10) | (pg['ast'] > 10)) 52 | 53 | (pg[['pts', 'ast']] > 10).sample(5) 54 | 55 | # Applying functions to columns 56 | def is_w_pac(team): 57 | """ 58 | Takes some string named team ('MIL', 'LAL', 'CHI' etc) and checks 59 | whether it's in the Pacific Division. 60 | """ 61 | return team in ['LAC', 'LAL', 'PHX', 'SAC', 'GSW'] 62 | 63 | pg['is_w_pac'] = pg['team'].apply(is_w_pac) 64 | 65 | pg[['name', 'team', 'is_w_pac']].sample(5) 66 | 67 | pg['is_w_pac_alternate'] = pg['team'].apply( 68 | lambda x: x in ['LAC', 'LAL', 'PHX', 'SAC', 'GSW']) 69 | 70 | # Dropping Columns 71 | pg.drop('is_w_pac_alternate', axis=1, inplace=True) 72 | 73 | # Renaming Columns 74 | pg.columns = [x.upper() for x in pg.columns] 75 | 76 | pg.head() 77 | 78 | pg.columns = [x.lower() for x in pg.columns] 79 | 80 | pg.rename(columns={'fgm': 'field_goals_made'}, inplace=True) 81 | 82 | # missing data 83 | pg['ft_pct'] = pg['ftm']/pg['fta'] 84 | pg[['name', 'team', 'ftm', 'fta', 'ft_pct']].head() 85 | 86 | pg['ft_pct'].isnull().head() 87 | 88 | pg['ft_pct'].notnull().head() 89 | 90 | pg['ft_pct'].fillna(-99).head() 91 | 92 | # Changing column types 93 | pg['date'].sample(5) 94 | 95 | date = '20191119' 96 | 97 | year = date[0:4] 98 | month = date[4:6] 99 | day = date[6:8] 100 | 101 | year 102 | month 103 | day 104 | 105 | # pg['month'] = pg['date'].str[4:6] # commented out since it gives an error 106 | 107 | pg['month'] = pg['date'].astype(str).str[4:6] 108 | pg[['name', 'team', 'month', 'date']].sample(5) 109 | 110 | pg['month'].astype(int).sample(5) 111 | 112 | pg.dtypes.head() 113 | -------------------------------------------------------------------------------- /code/03_02_functions.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from os import path 3 | 4 | # change this to the directory where the csv files that come with the book are 5 | # stored 6 | # on Windows it might be something like 'C:/mydir' 7 | 8 | DATA_DIR = './data' 9 | 10 | # load player-game data 11 | pg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 12 | 13 | pg[['game_id', 'player_id', 'date']] = ( 14 | pg[['game_id', 'player_id', 'date']].astype(str)) 15 | 16 | # book picks up here: 17 | pg[['fgm', 'fga', 'fg_pct', 'pts', 'fg3m', 'fg3a', 'fg3_pct']].mean() 18 | pg[['fgm', 'fga', 'pts', 'fg3m', 'fg3a']].max() 19 | 20 | pg.max() 21 | 22 | # Axis 23 | pg[['pts', 'ast', 'stl', 'reb']].mean(axis=0) 24 | pg[['pts', 'ast', 'stl', 'reb']].mean(axis=1).head() 25 | 26 | # Summary functions on boolean columns 27 | pg['cold_shooting'] = (pg['fga'] > 10) & (pg['pts'] < 5) 28 | pg['cold_shooting'].mean() 29 | 30 | pg['cold_shooting'].sum() 31 | 32 | (pg['fg3a'] > 30).any() 33 | (pg['fg3a'] > 20).any() 34 | 35 | (pg['min'] > 0).all() 36 | 37 | (pg[['pts', 'ast', 'reb', 'stl', 'blk']] >= 10).any(axis=1) 38 | 39 | pg['triple_double'] = ((pg[['pts', 'ast', 'reb', 'stl', 'blk']] >= 10) 40 | .sum(axis=1) >= 3) 41 | 42 | (pg[['pts', 'ast', 'reb', 'stl', 'blk']] >= 10).head() 43 | (pg[['pts', 'ast', 'reb', 'stl', 'blk']] >= 10).sum(axis=1).head() 44 | ((pg[['pts', 'ast', 'reb', 'stl', 'blk']] >= 10).sum(axis=1) >= 3).head() 45 | 46 | pg['triple_double'].sum() 47 | 48 | # Other misc built-in summary functions 49 | pg['pos'].value_counts() 50 | 51 | pg['pos'].value_counts(normalize=True) 52 | 53 | pd.crosstab(pg['team'], pg['pos']).head() 54 | -------------------------------------------------------------------------------- /code/03_03_filter.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from os import path 4 | 5 | # change this to the directory where the csv files that come with the book are 6 | # stored 7 | # on Windows it might be something like 'C:/mydir' 8 | 9 | DATA_DIR = './data' 10 | 11 | # note: we're passing the index_col argument, which immediately setting the 12 | # index to be the player_id column 13 | 14 | dfp = pd.read_csv(path.join(DATA_DIR, 'players.csv'), index_col='player_id') 15 | 16 | # Filtering 17 | lebron_id = 2544 18 | dfp.loc[lebron_id] 19 | 20 | laker_ids = ([2544, 203076, 201566]) 21 | 22 | dfp.loc[laker_ids] 23 | dfp.loc[laker_ids, ['name', 'school', 'height', 'weight']] 24 | dfp.loc[laker_ids, 'name'] 25 | 26 | # Boolean Indexing 27 | school_in_nc = dfp['school'] == 'North Carolina' 28 | 29 | school_in_nc.head() 30 | 31 | players_nc = dfp.loc[school_in_nc] 32 | 33 | players_nc[['name', 'school', 'height', 'weight']].head() 34 | players_duke = dfp.loc[dfp['school'] == 'Duke'] 35 | 36 | players_duke[['name', 'school', 'height', 'weight']].head() 37 | 38 | from_usa = dfp['country'] == 'USA' 39 | 40 | players_not_usa = dfp.loc[~from_usa] 41 | 42 | players_not_usa[['name', 'country', 'height', 'weight']].head() 43 | 44 | # Duplicates 45 | dfp.drop_duplicates(inplace=True) 46 | 47 | dfp.drop_duplicates('pos')[['name', 'pos', 'height', 'weight']] 48 | 49 | dfp.duplicated().head() 50 | 51 | dfp['pos'].duplicated().head() 52 | 53 | dfp.drop_duplicates('pos') 54 | dfp.loc[~dfp['pos'].duplicated()] 55 | 56 | # Combining filtering with changing columns 57 | dfp['draft_desc'] = '' 58 | dfp.loc[dfp['draft_round'] == 1, 'draft_desc'] = 'first round' 59 | dfp.loc[(dfp['draft_round'] == 1) & (dfp['draft_number'] <= 14), 'draft_desc'] = 'lottery' 60 | dfp.loc[(dfp['draft_round'] == 1) & (dfp['draft_number'] <= 5), 'draft_desc'] = 'top 5' 61 | dfp.loc[dfp['draft_round'] == 2, 'draft_desc'] = 'second round' 62 | dfp.loc[dfp['draft_round'].isnull(), 'draft_desc'] = 'undrafted' 63 | 64 | dfp[['name', 'school', 'draft_round', 'draft_number', 'draft_desc']].sample(5) 65 | 66 | # Query 67 | dfp.query("school == 'North Carolina'").head() 68 | 69 | dfp['school_in_kentucky'] = dfp['school'] == 'Kentucky' 70 | 71 | dfp.query("school_in_kentucky").head() 72 | 73 | dfp.query("draft_round.isnull()")[['name', 'school', 'draft_round', 'draft_number']].head() 74 | 75 | # note: if getting an error on line above, try it with engine='python' like 76 | # this 77 | dfp.query("draft_round.isnull()", engine='python')[['name', 'school', 'draft_round', 'draft_number']].head() 78 | -------------------------------------------------------------------------------- /code/03_04_granularity.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from os import path 3 | 4 | # change this to the directory where the csv files that come with the book are 5 | # stored 6 | # on Windows it might be something like 'C:/mydir' 7 | 8 | DATA_DIR = './data' 9 | 10 | shots = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) # shot data 11 | 12 | # make attempt and made variables 13 | shots['fg3m'] = (shots['value'] == 3) & shots['made'] 14 | shots['fg3a'] = (shots['value'] == 3) 15 | shots['fga'] = 1 16 | shots['fgm'] = shots['made'] 17 | 18 | # Granularity 19 | # Grouping 20 | shots.groupby('game_id').sum().head() # book picks up here 21 | 22 | fg_cols = ['fgm', 'fga', 'fg3m', 'fg3a'] 23 | 24 | shots.groupby('game_id').sum()[fg_cols].head() 25 | 26 | shots.groupby('game_id').agg({ 27 | 'value': 'mean', 28 | 'fgm': 'sum', 29 | 'fga': 'sum', 30 | 'fg3m': 'sum', 31 | 'fg3a': 'sum', 32 | }).head() 33 | 34 | shots.groupby('game_id').agg( 35 | ave_value = ('value', 'mean'), 36 | ave_dist= ('dist', 'mean'), 37 | max_dist = ('dist', 'max'), 38 | fgm = ('fgm', 'sum'), 39 | fga = ('fga', 'sum'), 40 | fg3m = ('fg3m', 'sum'), 41 | fg3a = ('fg3a', 'sum')).head() 42 | 43 | shots_per_pg = shots.groupby(['game_id', 'player_id']).agg( 44 | name = ('name', 'first'), 45 | fgm = ('fgm', 'sum'), 46 | fga = ('fga', 'sum'), 47 | fg3m = ('fg3m', 'sum'), 48 | fg3a = ('fg3a', 'sum'), 49 | ave_dist= ('dist', 'mean'), 50 | max_dist = ('dist', 'max'), 51 | ) 52 | 53 | shots_per_pg.head() 54 | 55 | # A note on multilevel indexing 56 | shots_per_pg.loc[[(21900002, 2544), (21900008, 201143)]] 57 | 58 | # Stacking and unstacking data 59 | sv = (shots 60 | .groupby(['team', 'value'])['made'] 61 | .sum() 62 | .reset_index()) 63 | 64 | sv.head() 65 | 66 | sv_reshaped = sv.set_index(['team', 'value']).unstack() 67 | sv_reshaped.head() 68 | 69 | total_made = sv_reshaped.sum(axis=1) 70 | total_made.head() 71 | 72 | sv_reshaped.columns = [2, 3] 73 | (sv_reshaped[3]/total_made).head() 74 | 75 | sv_reshaped.stack().head() 76 | 77 | -------------------------------------------------------------------------------- /code/03_05_combine.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from os import path 3 | 4 | # change this to the directory where the csv files that come with the book are 5 | # stored 6 | # on Windows it might be something like 'C:/mydir' 7 | 8 | DATA_DIR = './data' 9 | 10 | pg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) # player-game 11 | games = pd.read_csv(path.join(DATA_DIR, 'games.csv')) # game info 12 | player = pd.read_csv(path.join(DATA_DIR, 'players.csv')) # player info 13 | 14 | pg['fg2m'] = pg['fgm'] - pg['fg3m'] 15 | pg['fg2a'] = pg['fga'] - pg['fg3a'] 16 | 17 | # player game data 18 | pg[['name', 'team', 'game_id', 'fga', 'ast', 'stl', 'pts']].head(5) 19 | 20 | # game table 21 | games[['game_id', 'date', 'home', 'away']].head() 22 | 23 | # chapter picks up here: 24 | # Merge Question 1. What columns are you joining on? 25 | pd.merge(pg, games[['game_id', 'home', 'away']], on='game_id').head() 26 | 27 | pg['fg2m'] = pg['fgm'] - pg['fg3m'] 28 | pg['fg2a'] = pg['fga'] - pg['fg3a'] 29 | 30 | df2 = pg[['game_id', 'player_id', 'fg2m', 'fg2a']] 31 | df3 = pg[['game_id', 'player_id', 'fg3m', 'fg3a']] 32 | 33 | combined = pd.merge(df2, df3, on=['player_id', 'game_id']) 34 | 35 | # Merge Question 2. Are you doing a 1:1, 1:many (or many:1), or many:many 36 | # join?player.head() 37 | 38 | combined.head() 39 | 40 | player[['player_id', 'name', 'team', 'height', 'weight']].head() 41 | 42 | player['player_id'].duplicated().any() 43 | 44 | combined['player_id'].duplicated().any() 45 | 46 | pd.merge(combined, player[['player_id', 'name', 'height', 'weight']]).head() 47 | 48 | # pd.merge(combined, player, validate='1:1') # this will fail since it's 1:m 49 | 50 | # Merge Question 3. What are you doing with unmatched observations? 51 | df2 = pg.loc[pg['fg2a'] > 0, ['game_id', 'player_id', 'fg2m', 'fg2a']] 52 | 53 | df3 = pg.loc[pg['fg3a'] > 0, ['game_id', 'player_id', 'fg3m', 'fg3a']] 54 | 55 | df2.shape 56 | df3.shape 57 | 58 | comb_inner = pd.merge(df2, df3) 59 | comb_inner.shape 60 | 61 | comb_left = pd.merge(df2, df3, how='left') 62 | comb_left.shape 63 | 64 | comb_left.head() 65 | 66 | comb_outer = pd.merge(df2, df3, how='outer', indicator=True) 67 | comb_outer.shape 68 | 69 | comb_outer['_merge'].value_counts() 70 | 71 | # More on pd.merge 72 | # left_on and right_on 73 | df2.columns = ['game_id', 'fg2_shooter_id', 'fg2m', 'fg2a'] 74 | 75 | df3.columns = ['game_id', 'fg3_shooter_id', 'fg3m', 'fg3a'] 76 | 77 | pd.merge(df2, df3, left_on=['game_id', 'fg2_shooter_id'], 78 | right_on=['game_id', 'fg3_shooter_id']).head() 79 | 80 | # merging on index 81 | max_fg3 = (df3 82 | .groupby('fg3_shooter_id') 83 | .agg(max_fg3m = ('fg3m', 'max'), 84 | max_fg3a = ('fg3a', 'max'))) 85 | 86 | max_fg3.head() 87 | 88 | pd.merge(df3, max_fg3, left_on='fg3_shooter_id', right_index=True).sample(5) 89 | 90 | ############# 91 | # pd.concat() 92 | ############# 93 | df2 = (pg.loc[pg['fg2a'] > 0, ['game_id', 'player_id', 'fg2m', 'fg2a']] 94 | .set_index(['game_id', 'player_id'])) 95 | 96 | df3 = (pg.loc[pg['fg3a'] > 0, ['game_id', 'player_id', 'fg3m', 'fg3a']] 97 | .set_index(['game_id', 'player_id'])) 98 | 99 | df2.head() 100 | 101 | pd.concat([df2, df3], axis=1).head() 102 | 103 | df_ft = (pg.loc[pg['fta'] > 0, ['game_id', 'player_id', 'ftm', 'fta']] 104 | .set_index(['game_id', 'player_id'])) 105 | 106 | pd.concat([df2, df3, df_ft], axis=1).head() 107 | 108 | #### Combining DataFrames Vertically 109 | bucks = pg.loc[pg['team'] == 'MIL'] 110 | magic = pg.loc[pg['team'] == 'ORL'] 111 | 112 | bucks.shape 113 | magic.shape 114 | 115 | pd.concat([bucks, magic]).shape 116 | 117 | bucks_reset = bucks.reset_index(drop=True) 118 | magic_reset = magic.reset_index(drop=True) 119 | 120 | bucks_reset.head() 121 | 122 | pd.concat([bucks_reset, magic_reset]).sort_index().head() 123 | 124 | pd.concat([bucks_reset, magic_reset], ignore_index=True).sort_index().head() 125 | -------------------------------------------------------------------------------- /code/04_sql.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from os import path 3 | import sqlite3 4 | 5 | ############################################### 6 | # loading csvs and putting them in a sqlite db 7 | ############################################### 8 | 9 | # only need to run this section once 10 | 11 | # handle directories 12 | DATA_DIR = './data' 13 | 14 | # create connection 15 | conn = sqlite3.connect(path.join(DATA_DIR, 'basketball-data.sqlite')) 16 | 17 | # load csv data 18 | player_game = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 19 | player = pd.read_csv(path.join(DATA_DIR, 'players.csv')) 20 | game = pd.read_csv(path.join(DATA_DIR, 'games.csv')) 21 | team = pd.read_csv(path.join(DATA_DIR, 'teams.csv')) 22 | 23 | # and write it to sql 24 | player_game.to_sql('player_game', conn, index=False, if_exists='replace') 25 | player.to_sql('player', conn, index=False, if_exists='replace') 26 | game.to_sql('game', conn, index=False, if_exists='replace') 27 | team.to_sql('team', conn, index=False, if_exists='replace') 28 | 29 | ######### 30 | # Queries 31 | ######### 32 | conn = sqlite3.connect(path.join(DATA_DIR, 'basketball-data.sqlite')) 33 | 34 | # return entire player table 35 | df = pd.read_sql( 36 | """ 37 | SELECT * 38 | FROM player 39 | """, conn) 40 | df.head() 41 | 42 | # return specific columns from player table + rename on the fly 43 | df = pd.read_sql( 44 | """ 45 | SELECT player_id, name, birthdate as bday 46 | FROM player 47 | """, conn) 48 | df.head() 49 | 50 | ########### 51 | # filtering 52 | ########### 53 | 54 | # basic filter, only rows where team is MIA 55 | df = pd.read_sql( 56 | """ 57 | SELECT player_id, name, birthdate as bday 58 | FROM player 59 | WHERE country = 'Canada' 60 | """, conn) 61 | df.head() 62 | 63 | # AND in filter 64 | df = pd.read_sql( 65 | """ 66 | SELECT player_id, name, pos, country 67 | FROM player 68 | WHERE country != 'USA' AND pos == 'Center' 69 | """, conn) 70 | df.head() 71 | 72 | # OR in filter 73 | df = pd.read_sql( 74 | """ 75 | SELECT player_id, name, pos, country, school 76 | FROM player 77 | WHERE country != 'USA' OR school = 'North Carolina' 78 | """, conn) 79 | df.head() 80 | 81 | # IN in filter 82 | df = pd.read_sql( 83 | """ 84 | SELECT player_id, name, pos, country, school 85 | FROM player 86 | WHERE school IN ('North Carolina', 'Duke') 87 | """, conn) 88 | df.head() 89 | 90 | # negation with NOT 91 | df = pd.read_sql( 92 | """ 93 | SELECT player_id, name, pos, country, school 94 | FROM player 95 | WHERE school NOT IN ('North Carolina', 'Duke') 96 | """, conn) 97 | df.head() 98 | 99 | ######### 100 | # joining 101 | ######### 102 | 103 | # no WHERE so fullcrossjoin 104 | df = pd.read_sql( 105 | """ 106 | SELECT 107 | player.name, 108 | player.pos, 109 | player.team, 110 | team.conference, 111 | team.division 112 | FROM player, team 113 | """, conn) 114 | df.head(10) 115 | 116 | # add in two team columns to make clearer 117 | df = pd.read_sql( 118 | """ 119 | SELECT 120 | player.name, 121 | player.pos, 122 | player.team as player_team, 123 | team.team as team_team, 124 | team.conference, 125 | team.division 126 | FROM player, team 127 | """, conn) 128 | df.head(10) 129 | 130 | # n of rows 131 | df.shape 132 | 133 | # works when we add WHERE to filter after crossjoin 134 | df = pd.read_sql( 135 | """ 136 | SELECT 137 | player.name, 138 | player.pos, 139 | player.team, 140 | team.conference, 141 | team.division 142 | FROM player, team 143 | WHERE player.team = team.team 144 | """, conn) 145 | df.head() 146 | 147 | # add in team column to make clearer how it works 148 | df = pd.read_sql( 149 | """ 150 | SELECT 151 | player.name, 152 | player.pos, 153 | player.team as player_team, 154 | team.team as team_team, 155 | team.conference, 156 | team.division 157 | FROM player, team 158 | WHERE player.team = team.team 159 | """, conn) 160 | df.head() 161 | 162 | # adding a third table 163 | df = pd.read_sql( 164 | """ 165 | SELECT 166 | player.name, 167 | player.pos, 168 | team.team, 169 | team.conference, 170 | team.division, 171 | player_game.* 172 | FROM player, team, player_game 173 | WHERE 174 | player.team = team.team AND 175 | player_game.player_id = player.player_id 176 | """, conn) 177 | df.head() 178 | 179 | # adding a third table - shorthand 180 | df = pd.read_sql( 181 | """ 182 | SELECT 183 | p.name, 184 | p.pos, 185 | t.team, 186 | t.conference, 187 | t.division, 188 | pg.* 189 | FROM player AS p, team AS t, player_game AS pg 190 | WHERE 191 | p.team = t.team AND 192 | pg.player_id = p.player_id 193 | """, conn) 194 | df.head() 195 | 196 | # adding an additional filter 197 | df = pd.read_sql( 198 | """ 199 | SELECT 200 | p.name, 201 | p.pos, 202 | t.team, 203 | t.conference, 204 | t.division, 205 | pg.* 206 | FROM player AS p, team AS t, player_game AS pg 207 | WHERE 208 | p.team = t.team AND 209 | pg.player_id = p.player_id AND 210 | p.pos == 'Center' 211 | """, conn) 212 | df.head() 213 | 214 | ########### 215 | # LIMIT/TOP 216 | ########### 217 | 218 | # SELECT * 219 | # FROM player 220 | # LIMIT 5 221 | 222 | # SELECT TOP 5 * 223 | # FROM player 224 | 225 | df = pd.read_sql( 226 | """ 227 | SELECT DISTINCT season, date 228 | FROM game 229 | """, conn) 230 | df.head() 231 | 232 | # UNION 233 | # SUBQUERIES 234 | # LEFT, RIGHT, OUTER JOINS 235 | 236 | # SELECT * 237 | # FROM 238 | # LEFT JOIN ON . = . 239 | 240 | df = pd.read_sql( 241 | """ 242 | SELECT a.date, a.team, a.opp, a.first, a.last, b.pts, b.plus_minus 243 | FROM 244 | (SELECT game_id, date, home as team, away as opp, player_id, player.name, last, first 245 | FROM game, player 246 | WHERE 247 | game.home = player.team 248 | UNION 249 | SELECT game_id, date, home as team, away as opp, player_id, player.name, last, first 250 | FROM game, player 251 | WHERE 252 | game.away = player.team) AS a 253 | LEFT JOIN player_game AS b ON a.game_id = b.game_id AND a.player_id = b.player_id 254 | """, conn) 255 | 256 | df.query("last == 'Redick'") 257 | 258 | # df = pd.read_sql( 259 | # """ 260 | # SELECT game_id, home as team, first, last 261 | # FROM game, player 262 | # WHERE 263 | # game.home = player.team 264 | # UNION 265 | # SELECT game_id, home as team, first, last 266 | # FROM game, player 267 | # WHERE 268 | # game.away = player.team 269 | # """, conn) 270 | 271 | -------------------------------------------------------------------------------- /code/05_01_scraping.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup as Soup 2 | 3 | table_html = """ 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
NameDateTeamOppPtsReb
Lebron James2019-10-22LALLAC1810
Giannis Antetokounmpo2020-10-06MILSAN2412
31 | 32 | """ 33 | 34 | html_soup = Soup(table_html) 35 | 36 | tr_tag = html_soup.find('tr') 37 | tr_tag 38 | type(tr_tag) 39 | 40 | table_tag = html_soup.find('table') 41 | type(table_tag) 42 | 43 | td_tag = html_soup.find('td') 44 | td_tag 45 | type(td_tag) 46 | 47 | td_tag 48 | td_tag.string 49 | str(td_tag.string) 50 | 51 | tr_tag.find_all('th') 52 | 53 | [str(x.string) for x in tr_tag.find_all('th')] 54 | 55 | all_td_tags = table_tag.find_all('td') 56 | all_td_tags 57 | 58 | all_rows = table_tag.find_all('tr') 59 | first_data_row = all_rows[1] # 0 is header 60 | first_data_row.find_all('td') 61 | 62 | all_td_and_th_tags = table_tag.find_all(('td', 'th')) 63 | all_td_and_th_tags 64 | 65 | [str(x.string) for x in all_td_tags] 66 | 67 | all_rows = table_tag.find_all('tr') 68 | list_of_td_lists = [x.find_all('td') for x in all_rows[1:]] 69 | list_of_td_lists 70 | -------------------------------------------------------------------------------- /code/05_02_aba.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup as Soup 2 | import pandas as pd 3 | import requests 4 | from pandas import DataFrame 5 | 6 | response = requests.get('http://www.remembertheaba.com/ABAStatistics/ABATop20.html') 7 | 8 | print(response.text) 9 | 10 | aba_soup = Soup(response.text) 11 | 12 | # aba_soup is a nested tag, so call find_all on it 13 | 14 | tables = aba_soup.find_all('table') 15 | 16 | # find_all always returns a list, even if there's only one element, which is 17 | # the case here 18 | len(tables) 19 | 20 | # get the aba table out of it 21 | aba_table = tables[0] 22 | 23 | # aba_table another nested tag, so call find_all again 24 | rows = aba_table.find_all('tr') 25 | 26 | # this is a header row 27 | rows[1] 28 | 29 | # data rows 30 | first_data_row = rows[2] 31 | first_data_row 32 | 33 | # get columns from first_data_row 34 | first_data_row.find_all('td') 35 | 36 | # comprehension to get raw data out -- each x is simple tag 37 | [str(x.string) for x in first_data_row.find_all('td')] 38 | 39 | # put it in a function 40 | def parse_row(row): 41 | """ 42 | Take in a tr tag and get the data out of it in the form of a list of 43 | strings. 44 | """ 45 | return [str(x.string) for x in row.find_all('td')] 46 | 47 | # call function 48 | list_of_parsed_rows = [parse_row(row) for row in rows[2:]] 49 | 50 | # put it in a dataframe 51 | df = DataFrame(list_of_parsed_rows) 52 | df.head() 53 | 54 | # now lets get headers 55 | # could just do it manually 56 | df.columns = [str(x.string).lower() for x in rows[1].find_all('th')] 57 | 58 | str_cols = ['name', 'years'] 59 | float_cols = ['fg%', '3p%', 'ft%', 'ppg'] 60 | int_cols = [x for x in df.columns if not ((x in str_cols) or (x in float_cols))] 61 | 62 | df[float_cols] = df[float_cols].astype(float) 63 | 64 | # gives an error 65 | # df[int_cols] = df[float_cols].astype(int) 66 | 67 | # fix attempt 1 - commented out because it doesn't work 68 | # df[int_cols] = pd.to_numeric(df[int_cols], errors='coerce') 69 | 70 | # fix attempt 2 - call pd.numeric on one column at a time 71 | for int_col in int_cols: 72 | df[int_col] = pd.to_numeric(df[int_col], errors='coerce') 73 | 74 | 75 | # done 76 | df.head() 77 | 78 | -------------------------------------------------------------------------------- /code/05_03_api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | from pandas import DataFrame 4 | import pandas as pd 5 | 6 | BDL_API_KEY = 'PUT_YOUR_API_KEY_HERE' 7 | 8 | # players 9 | players_url = 'https://api.balldontlie.io/v1/players' 10 | 11 | AUTH_HEADER = {'Authorization': BDL_API_KEY} 12 | 13 | players_resp = requests.get(players_url, headers=AUTH_HEADER) 14 | players_json = players_resp.json() 15 | 16 | # with open('./data/json/players.json') as f: 17 | # players_json = json.load(f) 18 | 19 | players_json 20 | players_json.keys() 21 | 22 | type(players_json) 23 | 24 | type(players_json['data']) 25 | player0 = players_json['data'][0] 26 | player0 27 | 28 | player0_flat = {key: value for key, value in player0.items() if type(value) not 29 | in (dict, list)} 30 | 31 | player0_flat 32 | player0['team'] 33 | player0_flat['team_id'] = player0['team']['id'] 34 | player0_flat['team'] = player0['team']['abbreviation'] 35 | player0_flat['conference'] = player0['team']['conference'] 36 | 37 | player0_flat 38 | 39 | def flatten_player(nested): 40 | flat = {key: value for key, value in nested.items() if type(value) not in 41 | (dict, list)} 42 | flat['team_id'] = nested['team']['id'] 43 | flat['team'] = nested['team']['abbreviation'] 44 | flat['conference'] = nested['team']['conference'] 45 | 46 | return flat 47 | 48 | 49 | df_players = DataFrame([flatten_player(x) for x in players_json['data']]) 50 | df_players.head() 51 | 52 | # pagination 53 | players_json['meta'] 54 | 55 | players25 = (requests 56 | .get('https://api.balldontlie.io/v1/players/?cursor=25', headers=AUTH_HEADER) 57 | .json()) 58 | 59 | players25['meta'] 60 | 61 | players50_100 = (requests 62 | .get('https://api.balldontlie.io/v1/players/?cursor=25&per_page=100', headers=AUTH_HEADER) 63 | .json()) 64 | 65 | len(players50_100['data']) 66 | 67 | def get_players_cursor(cursor): 68 | players_json = (requests 69 | .get(f'https://api.balldontlie.io/v1/players/?cursor={cursor}', 70 | headers=AUTH_HEADER) 71 | .json()) 72 | 73 | return DataFrame([flatten_player(x) for x in players_json['data']]) 74 | 75 | df25 = get_players_cursor(25) 76 | df25.head() 77 | 78 | # return next cursor too 79 | 80 | def get_players_cursor_wnext(cursor): 81 | players_json = (requests 82 | .get(f'https://api.balldontlie.io/v1/players/?cursor={cursor}', 83 | headers=AUTH_HEADER) 84 | .json()) 85 | 86 | return ( 87 | DataFrame([flatten_player(x) for x in players_json['data']]), 88 | players_json['meta']['next_cursor']) 89 | 90 | df25, next = get_players_cursor_wnext(25) 91 | df25.head() 92 | 93 | players_json = (requests 94 | .get('https://api.balldontlie.io/v1/players/?cursor=100000000', 95 | headers=AUTH_HEADER) 96 | .json()) 97 | players_json 98 | 99 | if 'next_cursor' in players_json['meta']: 100 | next = players_json['meta']['next_cursor'] 101 | else: 102 | next = None 103 | 104 | next = players_json['meta'].get('next_cursor') 105 | next 106 | 107 | def get_players_cursor_wnext2(cursor): 108 | players_json = (requests 109 | .get(f'https://api.balldontlie.io/v1/players/?cursor={cursor}', 110 | headers=AUTH_HEADER) 111 | .json()) 112 | 113 | return ( 114 | DataFrame([flatten_player(x) for x in players_json['data']]), 115 | players_json['meta'].get('next_cursor')) 116 | 117 | 118 | i = 0 119 | while i < 5: 120 | print(i) 121 | i = i + 1 122 | 123 | df_all_players = DataFrame() 124 | 125 | cursor = 200 # start here - just showing how it works 126 | df_working, next_cursor = get_players_cursor_wnext2(cursor) 127 | 128 | while (next_cursor is not None) and (next_cursor <= 500): 129 | # add the current df_working_page to the dataframe of all the players 130 | df_all_players = pd.concat([df_all_players, df_working], 131 | ignore_index=True) 132 | 133 | # get the next page of data 134 | print(f'getting cursor {next_cursor}') 135 | df_working, next_cursor = get_players_cursor_wnext2(next_cursor) 136 | 137 | df_all_players.sample(10) 138 | df_all_players.shape 139 | 140 | 141 | ################################################################################ 142 | ################################################################################ 143 | 144 | ## note: this part isn't meant to be run 145 | ## i (nate) am running this Wed 5/1/24 to save data we'll load above 146 | ## 147 | ## including here to make it clearer this saved data above just comes from APIs 148 | 149 | # players_url = 'https://api.balldontlie.io/v1/players' 150 | 151 | # players_resp = requests.get(players_url, headers=AUTH_HEADER) 152 | 153 | # players_json = players_resp.json() 154 | 155 | # with open('./data/json/players.json', 'w') as f: 156 | # json.dump(players_json, f) 157 | -------------------------------------------------------------------------------- /code/05_04_wrapper.py: -------------------------------------------------------------------------------- 1 | from pandas import DataFrame, Series 2 | import pandas as pd 3 | 4 | # book starts here 5 | # starting with team annd player ids 6 | from nba_api.stats.static import teams, players 7 | 8 | player_df = DataFrame(players.get_players()) 9 | player_df.head() 10 | 11 | team_df = DataFrame(teams.get_teams()) 12 | team_df.head() 13 | 14 | from nba_api.stats.endpoints.commonplayerinfo import CommonPlayerInfo 15 | 16 | from nba_api.stats.endpoints.teamyearbyyearstats import TeamYearByYearStats 17 | 18 | team_df.head() 19 | 20 | pelicans_id = '1610612740' 21 | pelicans_data = TeamYearByYearStats(pelicans_id) 22 | pelicans_data 23 | 24 | pelicans_df = pelicans_data.get_data_frames()[0] 25 | pelicans_df.head() 26 | 27 | # play by play example 28 | from nba_api.stats.endpoints.playbyplayv2 import PlayByPlayV2 29 | PlayByPlayV2? 30 | 31 | 32 | game_id = '0021700807' 33 | pbp_data = PlayByPlayV2(game_id) 34 | pbp_data 35 | 36 | pbp_df = pbp_data.get_data_frames()[0] 37 | 38 | from nba_api.stats.endpoints.teamgamelog import TeamGameLog 39 | TeamGameLog? 40 | 41 | team_df.query("abbreviation == 'MIL'")[['id', 'full_name']] 42 | bucks_id = '1610612749' 43 | 44 | mil_log = TeamGameLog(bucks_id).get_data_frames()[0] 45 | 46 | mil_game_ids = list(mil_log['Game_ID'].head()) 47 | mil_game_ids 48 | 49 | mil_pbp_df1 = pd.concat([ 50 | PlayByPlayV2(x).get_data_frames()[0] for x in mil_game_ids], 51 | ignore_index=True) 52 | 53 | mil_pbp_df1.head() 54 | 55 | # commented out since it throws an error 56 | # PlayByPlayV2('XXXXXXXXXX') 57 | 58 | def get_pbp_data(game_id): 59 | try: 60 | pbp_df = PlayByPlayV2(game_id).get_data_frames()[0] 61 | except Exception as _: 62 | pbp_df = DataFrame() 63 | 64 | return pbp_df 65 | 66 | get_pbp_data('0021700807') 67 | get_pbp_data('XXXXXXXXXX') 68 | 69 | mil_game_ids = mil_game_ids + ['XXXXXXXXXX'] 70 | mil_game_ids 71 | 72 | pbp_df_all = DataFrame() 73 | bad_game_ids = [] 74 | for gid in mil_game_ids: 75 | print(gid) 76 | try: 77 | working_df = PlayByPlayV2(gid).get_data_frames()[0] 78 | except Exception as _: 79 | bad_game_ids = bad_game_ids + [gid] 80 | continue # continue means stop here and go to next item in the for loop 81 | 82 | pbp_df_all = pd.concat([pbp_df_all, working_df], ignore_index=True) 83 | 84 | pbp_df_all['GAME_ID'].value_counts() 85 | bad_game_ids 86 | 87 | -------------------------------------------------------------------------------- /code/06_01_summary.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | from os import path 5 | import random 6 | 7 | %matplotlib qt 8 | 9 | # directories 10 | DATA_DIR = './data' 11 | 12 | # load data 13 | dfs = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 14 | dfg = pd.read_csv(path.join(DATA_DIR, 'games.csv')) 15 | dft = pd.read_csv(path.join(DATA_DIR, 'teams.csv')) 16 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) 17 | 18 | # process data 19 | cats2 = ['layup', 'pullup', 'float', 'dunk', 'hook', 'fadeaway', 'step'] 20 | dfs['jump'] = dfs[cats2].sum(axis=1) == 0 21 | 22 | dfs['shot_type'] = pd.NA 23 | for shot in cats2 + ['jump']: 24 | dfs.loc[dfs[shot], 'shot_type'] = shot 25 | 26 | dfg.rename(columns={'home': 'home_team', 'away': 'away_team'}, inplace=True) 27 | 28 | # book picks up here: 29 | 30 | ############### 31 | # summary stats 32 | ############### 33 | 34 | # quantile function and describe 35 | 36 | dfs['dist'].quantile(.9) 37 | dfs[['dist', 'value']].describe() 38 | 39 | # % of shots 0-9 feet 40 | dfs['dist'].value_counts(normalize=True).sort_index().head(10) 41 | 42 | ########## 43 | # plotting 44 | ########## 45 | 46 | # basic displot 47 | # all on one line 48 | g = sns.FacetGrid(dfs).map(sns.kdeplot, 'dist', fill=True) 49 | 50 | # on seperate lines so it's clearer it's a two step process 51 | g = (sns.FacetGrid(dfs) 52 | .map(sns.kdeplot, 'dist', fill=True)) 53 | 54 | # invert axis 55 | g = (sns.FacetGrid(dfs) 56 | .map(sns.kdeplot, 'dist', fill=True)) 57 | g.set(xlim=(-5, 40)) 58 | g.ax.invert_xaxis() 59 | 60 | # density plot by shot type + made 61 | g = sns.displot(dfs, x='dist', kind='kde', hue='shot_type', fill=True) 62 | g.set(xlim=(-5, 40)) 63 | g.ax.invert_xaxis() 64 | plt.show() 65 | 66 | # too spiky, let's jitter it 67 | import random 68 | random.uniform(0, 1) 69 | 70 | [random.gauss(0, 1) for _ in range(10)] 71 | 72 | dfs['jdist'] = dfs['dist'] 73 | dfs.loc[dfs['shot_type'] == 'layup', 'jdist'] = dfs['dist'].apply(lambda x: x + 74 | random.gauss(0, 1)) 75 | dfs.loc[dfs['shot_type'] == 'dunk', 'jdist'] = dfs['dist'].apply(lambda x: x + 76 | random.gauss(0, 1)) 77 | 78 | g = (sns.FacetGrid(dfs, hue='shot_type') 79 | .map(sns.kdeplot, 'jdist', fill=True)) 80 | g.set(xlim=(-5, 40)) 81 | [ax[0].invert_xaxis() for ax in g.axes] 82 | g.add_legend() 83 | plt.show() 84 | 85 | g = (sns.FacetGrid(dfs, hue='shot_type', col='made') 86 | .map(sns.kdeplot, 'jdist', fill=True)) 87 | g.set(xlim=(-5, 40)) 88 | [ax[0].invert_xaxis() for ax in g.axes] 89 | g.add_legend() 90 | plt.show() 91 | 92 | # swap hue, col 93 | g = (sns.FacetGrid(dfs, col='shot_type', hue='made', col_wrap=3) 94 | .map(sns.kdeplot, 'jdist', fill=True)) 95 | g.set(xlim=(-5, 40)) 96 | [ax.invert_xaxis() for ax in g.axes] 97 | g.add_legend() 98 | plt.show() 99 | 100 | # lessons: matters, esp for hook shots; not as much for jumper 101 | 102 | ######################### 103 | # processing for plotting 104 | ######################### 105 | 106 | # example of reshaping data to get it into the shape we want 107 | 108 | # thinking about seaborn: specify seperate columns for columns, hue (color), 109 | # thing we're plotting (points) 110 | # so we need points in one column, then another type for scoring type 111 | 112 | # book picks up again here: 113 | dfg[['date', 'home_team', 'away_team', 'home_pts', 'away_pts']].head() 114 | 115 | def home_away_pts_df(df, location): 116 | df = df[['date', f'{location}_team', f'{location}_pts']].copy() 117 | df.columns = ['date', 'team', 'pts'] 118 | df['location'] = location 119 | return df 120 | 121 | home_away_pts_df(dfg, 'home').head() 122 | 123 | pts_long = pd.concat([ 124 | home_away_pts_df(dfg, loc) for loc in ['home', 'away']], ignore_index=True) 125 | 126 | # now can plot points by scoring system and position 127 | g = (sns.FacetGrid(pts_long, hue='location') 128 | .map(sns.kdeplot, 'pts', fill=True)) 129 | g.add_legend() 130 | plt.show() 131 | 132 | # now extend it to ALL columns 133 | 134 | location = 'home' 135 | opp = 'away' 136 | 137 | team_cols = [x for x in dfg.columns if x.startswith(location)] 138 | team_cols 139 | 140 | opp_cols = [x for x in dfg.columns if x.startswith(opp)] 141 | opp_cols 142 | 143 | df_team = dfg[team_cols] 144 | df_team.columns = [x.replace(f'{location}_', '') for x in df_team.columns] 145 | df_team.head() 146 | 147 | df_opp = dfg[opp_cols] 148 | df_opp.columns = [x.replace(opp, 'opp') for x in df_opp.columns] 149 | df_opp.head() 150 | 151 | dfg_wide = pd.concat([df_team, df_opp], axis=1) 152 | dfg_wide.head() 153 | 154 | def home_away_all(df, location): 155 | if location == 'home': 156 | opp = 'away' 157 | elif location == 'away': 158 | opp = 'home' 159 | 160 | team_cols = [x for x in df.columns if x.startswith(location)] 161 | team_cols 162 | 163 | opp_cols = [x for x in df.columns if x.startswith(opp)] 164 | opp_cols 165 | 166 | df_team = df[team_cols] 167 | df_team.columns = [x.replace(f'{location}_', '') for x in df_team.columns] 168 | df_team.head() 169 | 170 | df_opp = df[opp_cols] 171 | df_opp.columns = [x.replace(opp, 'opp') for x in df_opp.columns] 172 | df_opp.head() 173 | 174 | return pd.concat([df_team, df_opp], axis=1) 175 | 176 | dfg_wide = pd.concat([ 177 | home_away_all(dfg, 'home'), 178 | home_away_all(dfg, 'away')]) 179 | 180 | other_cols = [x for x in dfg.columns 181 | if not ((x.startswith('home')) or (x.startswith('away')))] 182 | other_cols 183 | 184 | df_all = pd.concat([dfg_wide, dfg[other_cols]], axis=1) 185 | df_all['win'] = dfg_wide['pts'] > dfg_wide['opp_pts'] 186 | 187 | df_all.head() 188 | 189 | # now we can do things like look at scoring by team/bubble 190 | g = (sns.FacetGrid(df_all, hue='bubble', col='team', col_wrap=5) 191 | .map(sns.kdeplot, 'pts', fill=True)) 192 | g.add_legend() 193 | 194 | ################################# 195 | # relationships between variables 196 | ################################# 197 | 198 | # fg pct vs pts 199 | sns.relplot(x='fg_pct', y='pts', data=df_all) 200 | plt.show() 201 | 202 | # fg pct vs pts - win/loss 203 | sns.relplot(x='fg_pct', y='pts', hue='win', data=df_all) 204 | plt.show() 205 | 206 | # fg pct vs pts - win/loss - by team 207 | sns.relplot(x='fg3_pct', y='pts', col='team', hue='win', col_wrap=5, data=df_all) 208 | g.add_legend() 209 | plt.show() 210 | 211 | ############# 212 | # correlation 213 | ############# 214 | 215 | df_all[['pts', 'reb', 'win', 'fg3_pct', 'fgm', 'blk', 'opp_fg3_pct']].corr().round(2) 216 | 217 | # scatter plot of 0.43 correlation 218 | g = sns.relplot(x='fg3_pct', y='fgm', data=df_all) 219 | plt.show() 220 | 221 | # scatter plot of -0.33 correlation 222 | g = sns.relplot(x='opp_fg3_pct', y='reb', data=df_all) 223 | plt.show() 224 | 225 | # scatter plot of 0.83 correlation 226 | g = sns.relplot(x='fgm', y='pts', data=df_all) 227 | plt.show() 228 | 229 | ######################## 230 | # line plots with python 231 | ######################## 232 | 233 | df_all = pd.merge(df_all, dft[['team', 'division', 'conference']]) 234 | 235 | # book picks back up here 236 | df_all['month'] = df_all['date'].str[5:7].astype(int) 237 | 238 | g = sns.relplot(x='month', y='pts', kind='line', data=df_all.query("~bubble"), 239 | hue='conference') 240 | plt.show() 241 | 242 | df_all.loc[(df_all['month'] == 11) & (df_all['conference'] == 'East'), ['date', 243 | 'team', 'opp_team', 'pts', 'opp_pts', 'conference']].sort_values('date').head() 244 | 245 | # max points by month and conference 246 | max_pts = (df_all.query("~bubble").groupby(['conference', 'month'], as_index=False)['pts'].max()) 247 | 248 | g = sns.relplot(x='month', y='pts', kind='line', style='conference', 249 | data=max_pts, hue='conference') 250 | plt.show() 251 | 252 | ############## 253 | # plot options 254 | ############## 255 | 256 | # basic plot 257 | g = (sns.FacetGrid(dfs, col='shot_type', hue='made') 258 | .map(sns.kdeplot, 'dist', fill=True)) 259 | 260 | # wrap columns 261 | g = (sns.FacetGrid(dfs, col='shot_type', hue='made', col_wrap=3) 262 | .map(sns.kdeplot, 'dist', fill=True)) 263 | 264 | # adding a title 265 | g.figure.subplots_adjust(top=0.9) # adding a title 266 | g.figure.suptitle('Distribution of Shot Distances by Type, Made') 267 | 268 | # modifying options 269 | g.set(xlim=(-5, 40)) 270 | 271 | g.set_xlabels('Ft') 272 | g.set_ylabels('Density') 273 | 274 | # saving 275 | g.savefig('shot_dist_type_made.png') 276 | 277 | -------------------------------------------------------------------------------- /code/06_02_shot_chart.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.image as mpimg 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import seaborn as sns 6 | from os import path 7 | 8 | pd.options.mode.chained_assignment = None 9 | %matplotlib qt 10 | 11 | # directories 12 | DATA_DIR = './data' 13 | 14 | # load data 15 | dfs = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 16 | dfg = pd.read_csv(path.join(DATA_DIR, 'games.csv')) 17 | dft = pd.read_csv(path.join(DATA_DIR, 'teams.csv')) 18 | 19 | # process data 20 | cats2 = ['layup', 'pullup', 'float', 'dunk', 'hook', 'fadeaway', 'step'] 21 | dfs['jump'] = dfs[cats2].sum(axis=1) == 0 22 | 23 | dfs['shot_type'] = np.nan 24 | for shot in cats2 + ['jump']: 25 | dfs.loc[dfs[shot], 'shot_type'] = shot 26 | 27 | ############# 28 | # shot charts 29 | ############# 30 | 31 | # Shot Charts As Seaborn Scatter Plots 32 | 33 | dfs[['name', 'dist', 'value', 'made', 'x', 'y']].head() 34 | 35 | # all shot data 36 | g = sns.relplot(data=dfs, x='x', y='y', kind='scatter', s=5) 37 | g.set(xlim=(-250, 250), ylim=(-50, 400), yticks=[], xticks=[], xlabel=None, 38 | ylabel=None) 39 | g.despine(left=True, bottom=True) 40 | 41 | # adding background 42 | map_img = mpimg.imread(path.join(DATA_DIR, 'nba_court.jpg')) 43 | 44 | g = sns.relplot(data=dfs, x='x', y='y', kind='scatter', s=5) 45 | g.set(xlim=(-250, 250), ylim=(-50, 400), yticks=[], xticks=[], xlabel=None, 46 | ylabel=None) 47 | g.despine(left=True, bottom=True) 48 | 49 | for ax in g.figure.axes: 50 | ax.imshow(map_img, zorder=0, extent=[-250, 250, -30, 400]) 51 | 52 | # putting in a function 53 | def shot_chart(dfs, **kwargs): 54 | g = sns.relplot(data=dfs, x='x', y='y', kind='scatter', **kwargs) 55 | g.set(xlim=(-250, 250), ylim=(-30, 400), yticks=[], xticks=[], xlabel=None, 56 | ylabel=None) 57 | g.despine(left=True, bottom=True) 58 | 59 | for ax in g.figure.axes: 60 | ax.imshow(map_img, zorder=0, extent=[-250, 250, -30, 400]) 61 | 62 | return g 63 | 64 | ### kwargs 65 | 66 | def add2(num1, num2): 67 | return num1 + num2 68 | 69 | # commented out - throws an error 70 | # add2(num1=4, num2=5, num3=1) 71 | 72 | def add2_flexible(num1, num2, **kwargs): 73 | return num1 + num2 74 | 75 | add2_flexible(num1=4, num2=5, num3=1, num4=4) 76 | 77 | # hue, s in **kwargs 78 | g = shot_chart(dfs, hue='made', style='made', s=15) 79 | 80 | g = shot_chart(dfs, hue='made', col='team', col_wrap=6, aspect=1.2, height=2) 81 | 82 | # do it for some single game 83 | g = shot_chart(dfs.query("game_id == 21900002"), hue='made', style='made', 84 | col='name', col_wrap=5, aspect=1.2, height=2) 85 | 86 | ### Contour Plots 87 | g = (sns.FacetGrid(dfs, col='shot_type', col_wrap=3) 88 | .map(sns.kdeplot, 'x', 'y', alpha=0.5, fill=True) 89 | .add_legend()) 90 | g.set(yticks=[], xticks=[], xlabel=None, ylabel=None) 91 | g.despine(left=True, bottom=True) 92 | for ax in g.figure.axes: 93 | ax.imshow(map_img, zorder=0, extent=[-250, 250, -30, 400]) 94 | -------------------------------------------------------------------------------- /code/07_01_ols.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import statsmodels.formula.api as smf 3 | from os import path 4 | 5 | DATA_DIR = './data' 6 | 7 | ################### 8 | # linear regression 9 | ################### 10 | 11 | # load 12 | df = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 13 | 14 | df['dist_sq'] = df['dist']**2 15 | df['made'] = df['made'].astype(int) 16 | 17 | df[['made', 'dist', 'dist_sq']].head() 18 | 19 | model = smf.ols(formula='made ~ dist + dist_sq', data=df) 20 | results = model.fit() 21 | 22 | results.summary2() 23 | 24 | def prob_of_make(yds): 25 | b0, b1, b2 = results.params 26 | return (b0 + b1*yds + b2*(yds**2)) 27 | 28 | prob_of_make(1) 29 | prob_of_make(25) 30 | prob_of_make(30) 31 | 32 | # process 33 | 34 | df['made_hat'] = results.predict(df) 35 | df[['made', 'made_hat']].head() 36 | 37 | model = smf.ols(formula='made ~ dist + dist_sq + value', data=df) 38 | results = model.fit() 39 | 40 | model = smf.ols(formula='made ~ dist + dist_sq + C(value)', data=df) 41 | results = model.fit() 42 | results.summary2() 43 | 44 | model = smf.ols(formula='made ~ dist + dist_sq + value', data=df) 45 | results = model.fit() 46 | results.summary2() 47 | -------------------------------------------------------------------------------- /code/07_02_coinflip.py: -------------------------------------------------------------------------------- 1 | import random 2 | from pandas import DataFrame 3 | import statsmodels.formula.api as smf 4 | 5 | 6 | coin = ['H', 'T'] 7 | 8 | # make empty DataFrame 9 | df = DataFrame(index=range(100)) 10 | 11 | # now fill it with a "guess" 12 | df['guess'] = [random.choice(coin) for _ in range(100)] 13 | 14 | # and flip 15 | df['result'] = [random.choice(coin) for _ in range(100)] 16 | 17 | # did we get it right or not? 18 | df['right'] = (df['guess'] == df['result']).astype(int) 19 | 20 | # regression 21 | model = smf.ols(formula='right ~ C(guess)', data=df) 22 | results = model.fit() 23 | results.summary2() 24 | 25 | random.randint(1, 10) 26 | -------------------------------------------------------------------------------- /code/07_03_ols2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import math 4 | from textwrap import dedent 5 | import statsmodels.formula.api as smf 6 | from os import path 7 | 8 | DATA_DIR = './data' 9 | 10 | df = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 11 | 12 | cats2 = ['layup', 'pullup', 'float', 'dunk', 'hook', 'fadeaway', 'step'] 13 | df['basic'] = df[cats2].sum(axis=1) == 0 14 | df['dist_sq'] = df['dist'] ** 2 15 | 16 | df['shot_type'] = np.nan 17 | for shot in cats2 + ['basic']: 18 | df.loc[df[shot], 'shot_type'] = shot 19 | 20 | df['made'] = df['made'].astype(int) 21 | 22 | ######################### 23 | # holding things constant 24 | ######################### 25 | 26 | # dunk: increases prob of making shot by 0.48 27 | model = smf.ols(formula= 28 | """ 29 | made ~ dunk 30 | """, data=df) 31 | results = model.fit() 32 | results.summary2() 33 | 34 | df.groupby('dunk')['dist'].mean() 35 | 36 | # fine but dunks close 37 | model = smf.ols(formula= 38 | """ 39 | made ~ dunk + dist 40 | """, data=df) 41 | results = model.fit() 42 | results.summary2() 43 | 44 | # let's add layup 45 | model = smf.ols(formula= 46 | """ 47 | made ~ dunk + dist + layup 48 | """, data=df) 49 | results = model.fit() 50 | results.summary2() 51 | 52 | 53 | 0.4913 -0.0055*18.5 54 | 55 | 0.4913 -0.0055*1 + 0.4273 56 | 0.4913 -0.0055*2 + 0.0669 57 | 58 | # interesting stuff with dist too 59 | 60 | cats = ['jump', 'hook', 'layup', 'driving', 'dunk', 'alley', 'reverse', 61 | 'turnaround', 'fadeaway', 'bank', 'finger', 'putback', 'float', 62 | 'pullup', 'step', 'cutting', 'tip'] 63 | 64 | df['ncats'] = df[cats].sum(axis=1) 65 | 66 | ############### 67 | # fixed effects 68 | ############### 69 | pd.get_dummies(df['shot_type']).head() 70 | 71 | model = smf.ols(formula="made ~ C(shot_type) + dist + dist_sq", data=df) 72 | results = model.fit() 73 | results.summary2() 74 | 75 | model = smf.ols( 76 | formula="made ~ C(shot_type, Treatment(reference='layup')) + dist + dist_sq", data=df) 77 | results = model.fit() 78 | results.summary2() 79 | 80 | b0 = 0.5715 81 | b_dist = -0.0135 82 | b_dist2 = 0.0001 83 | 84 | b0 + b_dist*25 + b_dist2*(25^2) + 0.0483 85 | 86 | b0 + b_dist*15 + b_dist2*(15^2) + 0.0212 87 | 88 | b0 + b_dist*3 + b_dist2*(3^2) 89 | 90 | #################### 91 | # squaring variables 92 | #################### 93 | 94 | df['dist2'] = df['dist'] ** 2 95 | model = smf.ols(formula="made ~ dist + dist2", data=df) 96 | results = model.fit() 97 | results.summary2() 98 | 99 | # cubed variables 100 | df['dist3'] = df['dist'] ** 3 101 | model = smf.ols(formula="made ~ dist + dist2 + dist3", data=df) 102 | results = model.fit() 103 | results.summary2() 104 | 105 | ############# 106 | # natural log 107 | ############# 108 | df['ln_dist'] = np.log(df['dist'].apply(lambda x: max(x, 0.5))) 109 | 110 | model = smf.ols(formula='made ~ ln_dist', data=df) 111 | results = model.fit() 112 | results.summary2() 113 | 114 | ############# 115 | # intractions 116 | ############# 117 | df['is_layup'] = df['shot_type'] == 'layup' 118 | 119 | model = smf.ols(formula= 120 | """ 121 | made ~ dist + dist:is_layup 122 | """, data=df) 123 | results = model.fit() 124 | results.summary2() 125 | 126 | 127 | model = smf.ols(formula= 128 | """ 129 | made ~ C(shot_type) + dist + dist:layup 130 | """, data=df) 131 | results = model.fit() 132 | results.summary2() 133 | 134 | model = smf.ols(formula= 135 | """ 136 | made ~ C(shot_type)*ln_dist 137 | """, data=df) 138 | results = model.fit() 139 | results.summary2() 140 | 141 | ####### 142 | # logit 143 | ####### 144 | model = smf.logit(formula= 145 | """ 146 | made ~ layup + dist + dist:layup 147 | """, data=df) 148 | logit_results = model.fit() 149 | logit_results.summary2() 150 | 151 | def prob_made_logit(dist, is_layup): 152 | b0, b1, b2, b3 = logit_results.params 153 | value = (b0 + b1*is_layup + b2*dist + b3*is_layup*dist) 154 | return 1/(1 + math.exp(-value)) 155 | 156 | prob_made_logit(0, 1) 157 | prob_made_logit(15, 0) 158 | prob_made_logit(2, 1) 159 | 160 | # have to look at range too 161 | prob_made(80, 0, 0) 162 | 163 | -------------------------------------------------------------------------------- /code/07_04_logit.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import math 3 | import statsmodels.formula.api as smf 4 | from os import path 5 | 6 | DATA_DIR = '/Users/nathan/fantasybook/data' 7 | 8 | ##################### 9 | # logistic regression 10 | ##################### 11 | 12 | # load 13 | df = pd.read_csv(path.join(DATA_DIR, 'play_data_sample.csv')) 14 | 15 | # process 16 | df = df.loc[(df['play_type'] == 'run') | (df['play_type'] == 'pass')] 17 | df['offensive_td'] = ((df['touchdown'] == 1) & (df['yards_gained'] > 0)) 18 | df['offensive_td'] = df['offensive_td'].astype(int) 19 | df['yardline_100_sq'] = df['yardline_100'] ** 2 20 | 21 | # run regression 22 | model = smf.logit(formula='offensive_td ~ yardline_100 + yardline_100_sq', 23 | data=df) 24 | results = model.fit() 25 | results.summary2() 26 | 27 | def prob_of_td(yds): 28 | b0, b1, b2 = results.params 29 | value = (b0 + b1*yds + b2*(yds**2)) 30 | return 1/(1 + math.exp(-value)) 31 | 32 | prob_of_td(75) 33 | prob_of_td(25) 34 | prob_of_td(5) 35 | -------------------------------------------------------------------------------- /code/07_05_random_forest.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from patsy import dmatrices 4 | from pandas import DataFrame, Series 5 | from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor 6 | from sklearn.linear_model import LogisticRegression 7 | from sklearn.model_selection import train_test_split, cross_val_score 8 | from os import path 9 | 10 | DATA_DIR = './data' 11 | 12 | df = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 13 | 14 | ###################################################### 15 | # processing - included extra comments to follow along 16 | ###################################################### 17 | 18 | # time left in quarter - decimal format 19 | df['time_left'] = df['min_left'] + df['sec_left']/60 20 | 21 | # we'll be categorizing shot type - currently in series of dummy columns 22 | # types to look at: 23 | shot_types = ['layup', 'pullup', 'float', 'dunk', 'hook', 'fadeaway'] 24 | 25 | # plan: 26 | # 1. create catch-all, "other" type that is NOT one of the above 27 | # 2. then go through and make new variable shot_type that is either layup, 28 | # pullup, etc or other 29 | 30 | # catch all other variable 31 | df['other'] = df[shot_types].sum(axis=1) == 0 32 | 33 | # create shot type variable 34 | df['shot_type'] = 'other' 35 | for shot in shot_types: 36 | df.loc[df[shot], 'shot_type'] = shot 37 | 38 | # book picks up here 39 | xvars = ['dist', 'x', 'y', 'period', 'time_left'] 40 | yvar = 'shot_type' 41 | 42 | df[xvars + [yvar]].sample(10) 43 | 44 | df[yvar].value_counts(normalize=True) 45 | 46 | # holdout set 47 | train, test = train_test_split(df, test_size=0.20) 48 | 49 | model = RandomForestClassifier(n_estimators=100) 50 | model.fit(train[xvars], train[yvar]) 51 | 52 | test['shot_type_hat'] = model.predict(test[xvars]) 53 | 54 | test['correct'] = (test['shot_type_hat'] == test['shot_type']) 55 | test['correct'].mean() 56 | 57 | model.predict_proba(test[xvars]) 58 | 59 | probs = DataFrame(model.predict_proba(test[xvars]), 60 | index=test.index, 61 | columns=model.classes_) 62 | 63 | probs.head() 64 | 65 | results = pd.concat([test[['name', 'dist', 'shot_type', 'shot_type_hat', 66 | 'correct']], probs], axis=1) 67 | 68 | results.groupby('shot_type')[['correct', 'layup', 'pullup', 'float', 'dunk', 69 | 'hook', 'fadeaway', 'other']].mean().round(2) 70 | 71 | # cross validation 72 | model = RandomForestClassifier(n_estimators=100) 73 | 74 | # note: cross validation takes a minute to run, if you're on an old computer 75 | # try changing cv to 5 or 3 76 | scores = cross_val_score(model, df[xvars], df[yvar], cv=10) 77 | 78 | scores 79 | scores.mean() 80 | 81 | # feature importance 82 | model = RandomForestClassifier(n_estimators=100) 83 | model.fit(df[xvars], df[yvar]) 84 | 85 | Series(model.feature_importances_, xvars).sort_values(ascending=False) 86 | 87 | 88 | dfs = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 89 | dfp = pd.read_csv(path.join(DATA_DIR, 'players.csv')) 90 | dfg = pd.read_csv(path.join(DATA_DIR, 'games.csv')) 91 | dfpg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 92 | dfb = pd.read_csv(path.join(DATA_DIR, 'box_adv.csv')) 93 | dft = pd.read_csv(path.join(DATA_DIR, 'teams.csv')) 94 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) 95 | 96 | ######################### 97 | # predicting wins example 98 | ######################### 99 | Index(['game_id', 'home', 'away', 'date', 'home_pts', 'away_pts', 'min', 100 | 'home_fgm', 'home_fga', 'home_fg_pct', 'home_fg3m', 'home_fg3a', 101 | 'home_fg3_pct', 'home_ftm', 'home_fta', 'home_ft_pct', 'home_oreb', 102 | 'home_dreb', 'home_reb', 'home_ast', 'home_stl', 'home_blk', 'home_tov', 103 | 'home_pf', 'home_plus_minus', 'away_fgm', 'away_fga', 'away_fg_pct', 104 | 'away_fg3m', 'away_fg3a', 'away_fg3_pct', 'away_ftm', 'away_fta', 105 | 'away_ft_pct', 'away_oreb', 'away_dreb', 'away_reb', 'away_ast', 106 | 'away_stl', 'away_blk', 'away_tov', 'away_pf', 'away_plus_minus', 107 | 'bubble', 'sample', 'season'], 108 | dtype='object') 109 | 110 | cols = ['home', 'away', 'home_pts', 'away_pts', 'home_fgm', 'home_fga', 111 | 'home_fg_pct', 'home_fg3m', 'home_fg3a', 'home_fg3_pct', 'home_ftm', 112 | 'home_fta', 'home_ft_pct', 'home_oreb', 'home_dreb', 'home_reb', 113 | 'home_ast', 'home_stl', 'home_blk', 'home_tov', 'home_pf', 114 | 'home_plus_minus', 'away_fgm', 'away_fga', 'away_fg_pct', 'away_fg3m', 115 | 'away_fg3a', 'away_fg3_pct', 'away_ftm', 'away_fta', 'away_ft_pct', 116 | 'away_oreb', 'away_dreb', 'away_reb', 'away_ast', 'away_stl', 117 | 'away_blk', 'away_tov', 'away_pf', 'away_plus_minus'] 118 | 119 | # includes 120 | # selecting and renaming columns, concatenating, grouping 121 | 122 | dfh = DataFrame(dfg[cols], copy=True) 123 | dfh['win'] = dfh['home_pts'] > dfh['away_pts'] 124 | dfh.columns = [x.replace('home', 'team') for x in dfh.columns] 125 | dfh.columns = [x.replace('away', 'opp') for x in dfh.columns] 126 | 127 | dfa = DataFrame(dfg[cols], copy=True) 128 | dfa['win'] = dfa['home_pts'] < dfa['away_pts'] 129 | dfa.columns = [x.replace('away', 'team') for x in dfa.columns] 130 | dfa.columns = [x.replace('home', 'opp') for x in dfa.columns] 131 | 132 | df = pd.concat([dfh, dfa], ignore_index=True) 133 | 134 | sum_cols = ['team_pts', 'opp_pts', 'team_fgm', 'team_fga', 'team_fg3m', 135 | 'team_fg3a', 'team_ftm', 'team_fta', 'team_oreb', 'team_dreb', 136 | 'team_reb', 'team_ast', 'team_stl', 'team_blk', 'team_tov', 'team_pf', 137 | 'team_plus_minus', 'opp_fgm', 'opp_fga', 'opp_fg3m', 'opp_fg3a', 138 | 'opp_ftm', 'opp_fta', 'opp_oreb', 'opp_dreb', 'opp_reb', 'opp_ast', 139 | 'opp_stl', 'opp_blk', 'opp_tov', 'opp_pf', 'opp_plus_minus', 'win'] 140 | 141 | df2 = df.groupby('team')[sum_cols].sum().reset_index() 142 | 143 | xvars = ['team_pts', 'opp_pts', 'team_fgm', 'team_fga', 'team_fg3m', 144 | 'team_fg3a', 'team_ftm', 'team_fta', 'team_oreb', 'team_dreb', 145 | 'team_reb', 'team_ast', 'team_stl', 'team_blk', 'team_tov', 'team_pf', 146 | 'opp_fgm', 'opp_fga', 'opp_fg3m', 'opp_fg3a', 147 | 'opp_ftm', 'opp_fta', 'opp_oreb', 'opp_dreb', 'opp_reb', 'opp_ast', 148 | 'opp_stl', 'opp_blk', 'opp_tov', 'opp_pf'] 149 | yvar = 'team' 150 | 151 | train, test = train_test_split(df, test_size=0.20) 152 | 153 | model = RandomForestClassifier(n_estimators=100) 154 | model.fit(train[xvars], train[yvar]) 155 | 156 | test['team_hat'] = model.predict(test[xvars]) 157 | test['correct'] = (test['team_hat'] == test['team']) 158 | test['correct'].mean() 159 | 160 | probs = DataFrame(model.predict_proba(test[xvars]), 161 | index=test.index, 162 | columns=model.classes_) 163 | probs.head() 164 | 165 | probs['actual'] = test['team'] 166 | 167 | model.fit(df2[xvars], df2[yvar]) # running model fitting on entire dataset 168 | Series(model.feature_importances_, xvars).sort_values(ascending=False) 169 | 170 | ################################################ 171 | # original example - predicting made or not made 172 | ################################################ 173 | 174 | cats2 = ['layup', 'pullup', 'float', 'dunk', 'hook', 'fadeaway', 'step'] 175 | dfs['basic'] = dfs[cats2].sum(axis=1) == 0 176 | 177 | dfs['shot_type'] = np.nan 178 | for shot in cats2 + ['basic']: 179 | dfs.loc[dfs[shot], 'shot_type'] = shot 180 | 181 | dfs['time_left'] = dfs['min_left'] + dfs['sec_left']/60 182 | 183 | # try shot type example 184 | xvars = ['dist', 'x', 'y', 'period', 'time_left'] 185 | yvar = 'shot_type' 186 | 187 | train, test = train_test_split(dfs, test_size=0.20) 188 | 189 | model = RandomForestClassifier(n_estimators=100) 190 | model.fit(train[xvars], train[yvar]) 191 | 192 | test['shot_type_hat'] = model.predict(test[xvars]) 193 | test['correct'] = (test['shot_type_hat'] == test['shot_type']) 194 | test['correct'].mean() 195 | 196 | model.predict_proba(test[xvars]) 197 | 198 | probs = DataFrame(model.predict_proba(test[xvars]), 199 | index=test.index, 200 | columns=model.classes_) 201 | 202 | probs.head() 203 | 204 | probs[['actual', 'correct']] = test[['shot_type', 'correct']] 205 | 206 | probs.head() 207 | 208 | probs.groupby('actual')[['correct', 'layup', 'pullup', 'float', 'dunk', 209 | 'hook', 'fadeaway', 'step', 'basic']].mean().round(2) 210 | 211 | # cross validation 212 | model = RandomForestClassifier(n_estimators=100) 213 | scores = cross_val_score(model, dfs[xvars], dfs[yvar], cv=10) 214 | 215 | scores 216 | scores.mean() 217 | 218 | model = RandomForestClassifier(n_estimators=100) 219 | model.fit(dfs[xvars], dfs[yvar]) 220 | 221 | Series(model.feature_importances_, xvars).sort_values(ascending=False) 222 | 223 | # then add in player info - height, weight, year entered the league 224 | 225 | height = '6-6' 226 | height = height.split('-') 227 | ft = height[0] 228 | inches = height[1] 229 | height_dec = int(ft) + int(inches)/12 230 | 231 | def height_to_dec(height): 232 | height = height.split('-') 233 | ft = height[0] 234 | inches = height[1] 235 | return int(ft) + int(inches)/12 236 | 237 | dfp['height_dec'] = dfp['height'].apply(height_to_dec) 238 | 239 | dfs2 = pd.merge(dfs, dfp[['player_id', 'height_dec', 'weight', 'from_year']], 240 | how='left') 241 | 242 | xvars = ['ln_dist', 'x', 'y', 'period', 'min_left', 'sec_left', 'height_dec', 243 | 'weight', 'from_year'] 244 | yvar = 'shot_type' 245 | 246 | train, test = train_test_split(dfs2, test_size=0.20) 247 | 248 | model = RandomForestClassifier(n_estimators=100) 249 | model.fit(train[xvars], train[yvar]) 250 | 251 | test['shot_hat'] = model.predict(test[xvars]) 252 | test['correct'] = (test['shot_hat'] == test['shot_type']) 253 | test['correct'].mean() 254 | 255 | # probs.columns = ['pmiss', 'pmake'] 256 | 257 | # original 258 | 259 | dfs['ln_dist'] = np.log(dfs['dist'].apply(lambda x: max(x, 0.5))) 260 | 261 | xvars = ['ln_dist', 'value', 'x', 'y', 'running', 'jump', 'hook', 'layup', 262 | 'driving', 'dunk', 'alley', 'reverse', 'turnaround', 'fadeaway', 263 | 'bank', 'finger', 'putback', 'float', 'pullup', 'step', 'cutting', 264 | 'tip', 'period', 'min_left', 'sec_left'] 265 | 266 | yvar = 'made' 267 | 268 | train, test = train_test_split(dfs, test_size=0.20) 269 | 270 | model = RandomForestClassifier(n_estimators=100) 271 | model.fit(train[xvars], train[yvar]) 272 | 273 | test['made_hat'] = model.predict(test[xvars]) 274 | test['correct'] = (test['made_hat'] == test['made']) 275 | test['correct'].mean() 276 | 277 | model.predict_proba(test[xvars]) 278 | 279 | probs = DataFrame(model.predict_proba(test[xvars]), 280 | index=test.index, 281 | columns=model.classes_) 282 | probs.head() 283 | probs.columns = ['pmiss', 'pmake'] 284 | 285 | results = pd.concat([ 286 | test[['name', 'shot_id', 'shot_type', 'dist', 'made', 'made_hat', 287 | 'correct']], 288 | probs[['pmake']]], axis=1) 289 | 290 | 291 | results.sample(5) 292 | 293 | results.groupby(['shot_type', 'made'])['correct'].mean().to_frame().unstack() 294 | 295 | results['pmake_bin'] = pd.cut(results['pmake'], 10) 296 | 297 | results.groupby('pmake_bin')['made'].mean() 298 | 299 | # cross validation 300 | model = RandomForestClassifier(n_estimators=100) 301 | scores = cross_val_score(model, dfs[xvars], dfs[yvar], cv=10) 302 | 303 | scores 304 | scores.mean() 305 | 306 | # feature importance 307 | model.fit(dfs[xvars], dfs[yvar]) # running model fitting on entire dataset 308 | Series(model.feature_importances_, xvars).sort_values(ascending=False) 309 | 310 | # homework: add in some player info? 311 | 312 | # vs logit model 313 | dfs['ln_dist'] = np.log(dfs['dist'].apply(lambda x: max(x, 0.5))) 314 | dfs['made'] = dfs['made'].astype(int) 315 | y, X = dmatrices('made ~ dist', dfs) 316 | 317 | model = LogisticRegression() 318 | scores = cross_val_score(model, X, y, cv=10) 319 | 320 | scores 321 | scores.mean() 322 | 323 | # more 324 | -------------------------------------------------------------------------------- /data/basketball-data.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/data/basketball-data.sqlite -------------------------------------------------------------------------------- /data/json/players.json: -------------------------------------------------------------------------------- 1 | {"data": [{"id": 1, "first_name": "Alex", "last_name": "Abrines", "position": "G", "height": "6-6", "weight": "190", "jersey_number": "8", "college": "FC Barcelona", "country": "Spain", "draft_year": 2013, "draft_round": 2, "draft_number": 32, "team": {"id": 21, "conference": "West", "division": "Northwest", "city": "Oklahoma City", "name": "Thunder", "full_name": "Oklahoma City Thunder", "abbreviation": "OKC"}}, {"id": 2, "first_name": "Jaylen", "last_name": "Adams", "position": "G", "height": "6-0", "weight": "225", "jersey_number": "10", "college": "St. Bonaventure", "country": "USA", "draft_year": null, "draft_round": null, "draft_number": null, "team": {"id": 1, "conference": "East", "division": "Southeast", "city": "Atlanta", "name": "Hawks", "full_name": "Atlanta Hawks", "abbreviation": "ATL"}}, {"id": 3, "first_name": "Steven", "last_name": "Adams", "position": "C", "height": "6-11", "weight": "265", "jersey_number": "12", "college": "Pittsburgh", "country": "New Zealand", "draft_year": 2013, "draft_round": 1, "draft_number": 12, "team": {"id": 11, "conference": "West", "division": "Southwest", "city": "Houston", "name": "Rockets", "full_name": "Houston Rockets", "abbreviation": "HOU"}}, {"id": 4, "first_name": "Bam", "last_name": "Adebayo", "position": "C-F", "height": "6-9", "weight": "255", "jersey_number": "13", "college": "Kentucky", "country": "USA", "draft_year": 2017, "draft_round": 1, "draft_number": 14, "team": {"id": 16, "conference": "East", "division": "Southeast", "city": "Miami", "name": "Heat", "full_name": "Miami Heat", "abbreviation": "MIA"}}, {"id": 5, "first_name": "DeVaughn", "last_name": "Akoon-Purcell", "position": "G-F", "height": "6-5", "weight": "201", "jersey_number": "44", "college": "Illinois State", "country": "Trinidad and Tobago", "draft_year": 2016, "draft_round": null, "draft_number": null, "team": {"id": 8, "conference": "West", "division": "Northwest", "city": "Denver", "name": "Nuggets", "full_name": "Denver Nuggets", "abbreviation": "DEN"}}, {"id": 6, "first_name": "LaMarcus", "last_name": "Aldridge", "position": "F", "height": "6-11", "weight": "250", "jersey_number": "21", "college": "Texas", "country": "USA", "draft_year": 2006, "draft_round": 1, "draft_number": 2, "team": {"id": 3, "conference": "East", "division": "Atlantic", "city": "Brooklyn", "name": "Nets", "full_name": "Brooklyn Nets", "abbreviation": "BKN"}}, {"id": 7, "first_name": "Rawle", "last_name": "Alkins", "position": "G", "height": "6-5", "weight": "225", "jersey_number": "20", "college": "Arizona", "country": "USA", "draft_year": null, "draft_round": null, "draft_number": null, "team": {"id": 5, "conference": "East", "division": "Central", "city": "Chicago", "name": "Bulls", "full_name": "Chicago Bulls", "abbreviation": "CHI"}}, {"id": 8, "first_name": "Grayson", "last_name": "Allen", "position": "G", "height": "6-4", "weight": "198", "jersey_number": "8", "college": "Duke", "country": "USA", "draft_year": 2018, "draft_round": 1, "draft_number": 21, "team": {"id": 24, "conference": "West", "division": "Pacific", "city": "Phoenix", "name": "Suns", "full_name": "Phoenix Suns", "abbreviation": "PHX"}}, {"id": 9, "first_name": "Jarrett", "last_name": "Allen", "position": "C", "height": "6-9", "weight": "243", "jersey_number": "31", "college": "Texas", "country": "USA", "draft_year": 2017, "draft_round": 1, "draft_number": 22, "team": {"id": 6, "conference": "East", "division": "Central", "city": "Cleveland", "name": "Cavaliers", "full_name": "Cleveland Cavaliers", "abbreviation": "CLE"}}, {"id": 10, "first_name": "Al-Farouq", "last_name": "Aminu", "position": "F", "height": "6-8", "weight": "220", "jersey_number": "5", "college": "Wake Forest", "country": "USA", "draft_year": 2010, "draft_round": 1, "draft_number": 8, "team": {"id": 25, "conference": "West", "division": "Northwest", "city": "Portland", "name": "Trail Blazers", "full_name": "Portland Trail Blazers", "abbreviation": "POR"}}, {"id": 11, "first_name": "Justin", "last_name": "Anderson", "position": "G-F", "height": "6-5", "weight": "231", "jersey_number": "10", "college": "Virginia", "country": "USA", "draft_year": 2015, "draft_round": 1, "draft_number": 21, "team": {"id": 12, "conference": "East", "division": "Central", "city": "Indiana", "name": "Pacers", "full_name": "Indiana Pacers", "abbreviation": "IND"}}, {"id": 12, "first_name": "Kyle", "last_name": "Anderson", "position": "F", "height": "6-9", "weight": "230", "jersey_number": "1", "college": "UCLA", "country": "USA", "draft_year": 2014, "draft_round": 1, "draft_number": 30, "team": {"id": 18, "conference": "West", "division": "Northwest", "city": "Minnesota", "name": "Timberwolves", "full_name": "Minnesota Timberwolves", "abbreviation": "MIN"}}, {"id": 13, "first_name": "Ryan", "last_name": "Anderson", "position": "F", "height": "6-10", "weight": "240", "jersey_number": "31", "college": "California", "country": "USA", "draft_year": 2008, "draft_round": 1, "draft_number": 21, "team": {"id": 19, "conference": "West", "division": "Southwest", "city": "New Orleans", "name": "Pelicans", "full_name": "New Orleans Pelicans", "abbreviation": "NOP"}}, {"id": 14, "first_name": "Ike", "last_name": "Anigbogu", "position": "C", "height": "6-10", "weight": "262", "jersey_number": "13", "college": "UCLA", "country": "USA", "draft_year": 2017, "draft_round": 2, "draft_number": 47, "team": {"id": 12, "conference": "East", "division": "Central", "city": "Indiana", "name": "Pacers", "full_name": "Indiana Pacers", "abbreviation": "IND"}}, {"id": 15, "first_name": "Giannis", "last_name": "Antetokounmpo", "position": "F", "height": "6-11", "weight": "243", "jersey_number": "34", "college": "Filathlitikos", "country": "Greece", "draft_year": 2013, "draft_round": 1, "draft_number": 15, "team": {"id": 17, "conference": "East", "division": "Central", "city": "Milwaukee", "name": "Bucks", "full_name": "Milwaukee Bucks", "abbreviation": "MIL"}}, {"id": 16, "first_name": "Kostas", "last_name": "Antetokounmpo", "position": "F", "height": "6-10", "weight": "200", "jersey_number": "37", "college": "Dayton", "country": "Greece", "draft_year": 2018, "draft_round": 2, "draft_number": 60, "team": {"id": 5, "conference": "East", "division": "Central", "city": "Chicago", "name": "Bulls", "full_name": "Chicago Bulls", "abbreviation": "CHI"}}, {"id": 17, "first_name": "Carmelo", "last_name": "Anthony", "position": "F", "height": "6-7", "weight": "238", "jersey_number": "7", "college": "Syracuse", "country": "USA", "draft_year": 2003, "draft_round": 1, "draft_number": 3, "team": {"id": 14, "conference": "West", "division": "Pacific", "city": "Los Angeles", "name": "Lakers", "full_name": "Los Angeles Lakers", "abbreviation": "LAL"}}, {"id": 18, "first_name": "OG", "last_name": "Anunoby", "position": "F", "height": "6-7", "weight": "240", "jersey_number": "8", "college": "Indiana", "country": "United Kingdom", "draft_year": 2017, "draft_round": 1, "draft_number": 23, "team": {"id": 20, "conference": "East", "division": "Atlantic", "city": "New York", "name": "Knicks", "full_name": "New York Knicks", "abbreviation": "NYK"}}, {"id": 19, "first_name": "Ryan", "last_name": "Arcidiacono", "position": "G", "height": "6-3", "weight": "195", "jersey_number": "51", "college": "Villanova", "country": "USA", "draft_year": null, "draft_round": null, "draft_number": null, "team": {"id": 20, "conference": "East", "division": "Atlantic", "city": "New York", "name": "Knicks", "full_name": "New York Knicks", "abbreviation": "NYK"}}, {"id": 20, "first_name": "Trevor", "last_name": "Ariza", "position": "F", "height": "6-8", "weight": "215", "jersey_number": "8", "college": "UCLA", "country": "USA", "draft_year": 2004, "draft_round": 2, "draft_number": 43, "team": {"id": 14, "conference": "West", "division": "Pacific", "city": "Los Angeles", "name": "Lakers", "full_name": "Los Angeles Lakers", "abbreviation": "LAL"}}, {"id": 21, "first_name": "D.J.", "last_name": "Augustin", "position": "G", "height": "5-11", "weight": "183", "jersey_number": "4", "college": "Texas", "country": "USA", "draft_year": 2008, "draft_round": 1, "draft_number": 9, "team": {"id": 14, "conference": "West", "division": "Pacific", "city": "Los Angeles", "name": "Lakers", "full_name": "Los Angeles Lakers", "abbreviation": "LAL"}}, {"id": 22, "first_name": "Deandre", "last_name": "Ayton", "position": "C", "height": "7-0", "weight": "247", "jersey_number": "2", "college": "Arizona", "country": "Bahamas", "draft_year": 2018, "draft_round": 1, "draft_number": 1, "team": {"id": 25, "conference": "West", "division": "Northwest", "city": "Portland", "name": "Trail Blazers", "full_name": "Portland Trail Blazers", "abbreviation": "POR"}}, {"id": 23, "first_name": "Dwayne", "last_name": "Bacon", "position": "G-F", "height": "6-6", "weight": "221", "jersey_number": "8", "college": "Florida State", "country": "USA", "draft_year": 2017, "draft_round": 2, "draft_number": 40, "team": {"id": 14, "conference": "West", "division": "Pacific", "city": "Los Angeles", "name": "Lakers", "full_name": "Los Angeles Lakers", "abbreviation": "LAL"}}, {"id": 24, "first_name": "Marvin", "last_name": "Bagley III", "position": "F", "height": "6-10", "weight": "235", "jersey_number": "35", "college": "Duke", "country": "USA", "draft_year": 2018, "draft_round": 1, "draft_number": 2, "team": {"id": 30, "conference": "East", "division": "Southeast", "city": "Washington", "name": "Wizards", "full_name": "Washington Wizards", "abbreviation": "WAS"}}, {"id": 25, "first_name": "Ron", "last_name": "Baker", "position": "G", "height": "6-4", "weight": "220", "jersey_number": "31", "college": "Wichita State", "country": "USA", "draft_year": null, "draft_round": null, "draft_number": null, "team": {"id": 20, "conference": "East", "division": "Atlantic", "city": "New York", "name": "Knicks", "full_name": "New York Knicks", "abbreviation": "NYK"}}], "meta": {"next_cursor": 25, "per_page": 25}} -------------------------------------------------------------------------------- /data/json/teams.csv: -------------------------------------------------------------------------------- 1 | id,full_name,abbreviation,nickname,city,state,year_founded 2 | 1610612737,Atlanta Hawks,ATL,Hawks,Atlanta,Atlanta,1949 3 | 1610612738,Boston Celtics,BOS,Celtics,Boston,Massachusetts,1946 4 | 1610612739,Cleveland Cavaliers,CLE,Cavaliers,Cleveland,Ohio,1970 5 | 1610612740,New Orleans Pelicans,NOP,Pelicans,New Orleans,Louisiana,2002 6 | 1610612741,Chicago Bulls,CHI,Bulls,Chicago,Illinois,1966 7 | 1610612742,Dallas Mavericks,DAL,Mavericks,Dallas,Texas,1980 8 | 1610612743,Denver Nuggets,DEN,Nuggets,Denver,Colorado,1976 9 | 1610612744,Golden State Warriors,GSW,Warriors,Golden State,California,1946 10 | 1610612745,Houston Rockets,HOU,Rockets,Houston,Texas,1967 11 | 1610612746,Los Angeles Clippers,LAC,Clippers,Los Angeles,California,1970 12 | 1610612747,Los Angeles Lakers,LAL,Lakers,Los Angeles,California,1948 13 | 1610612748,Miami Heat,MIA,Heat,Miami,Florida,1988 14 | 1610612749,Milwaukee Bucks,MIL,Bucks,Milwaukee,Wisconsin,1968 15 | 1610612750,Minnesota Timberwolves,MIN,Timberwolves,Minnesota,Minnesota,1989 16 | 1610612751,Brooklyn Nets,BKN,Nets,Brooklyn,New York,1976 17 | 1610612752,New York Knicks,NYK,Knicks,New York,New York,1946 18 | 1610612753,Orlando Magic,ORL,Magic,Orlando,Florida,1989 19 | 1610612754,Indiana Pacers,IND,Pacers,Indiana,Indiana,1976 20 | 1610612755,Philadelphia 76ers,PHI,76ers,Philadelphia,Pennsylvania,1949 21 | 1610612756,Phoenix Suns,PHX,Suns,Phoenix,Arizona,1968 22 | 1610612757,Portland Trail Blazers,POR,Trail Blazers,Portland,Oregon,1970 23 | 1610612758,Sacramento Kings,SAC,Kings,Sacramento,California,1948 24 | 1610612759,San Antonio Spurs,SAS,Spurs,San Antonio,Texas,1976 25 | 1610612760,Oklahoma City Thunder,OKC,Thunder,Oklahoma City,Oklahoma,1967 26 | 1610612761,Toronto Raptors,TOR,Raptors,Toronto,Ontario,1995 27 | 1610612762,Utah Jazz,UTA,Jazz,Utah,Utah,1974 28 | 1610612763,Memphis Grizzlies,MEM,Grizzlies,Memphis,Tennessee,1995 29 | 1610612764,Washington Wizards,WAS,Wizards,Washington,District of Columbia,1961 30 | 1610612765,Detroit Pistons,DET,Pistons,Detroit,Michigan,1948 31 | 1610612766,Charlotte Hornets,CHA,Hornets,Charlotte,North Carolina,1988 32 | -------------------------------------------------------------------------------- /data/nba_court.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/data/nba_court.jpg -------------------------------------------------------------------------------- /data/problems/combine1/def.csv: -------------------------------------------------------------------------------- 1 | player_id,game_id,stl,blk 2 | 2544,21900002,1,1 3 | 2730,21900002,0,1 4 | 101150,21900002,1,0 5 | 201580,21900002,0,2 6 | 201976,21900002,0,1 7 | 201980,21900002,2,1 8 | 202695,21900002,2,1 9 | 203076,21900002,1,2 10 | 203090,21900002,4,2 11 | 1626149,21900002,1,1 12 | 201143,21900008,1,0 13 | 202699,21900008,2,0 14 | 203954,21900008,0,3 15 | 1626196,21900008,1,2 16 | 1627759,21900008,1,0 17 | 1628369,21900008,2,0 18 | 1628464,21900008,0,1 19 | 1629057,21900008,1,1 20 | 1629680,21900008,2,2 21 | 2544,21900054,0,1 22 | 2730,21900054,1,2 23 | 201162,21900054,1,0 24 | 201580,21900054,0,1 25 | 201980,21900054,1,0 26 | 202340,21900054,2,0 27 | 203076,21900054,0,2 28 | 203484,21900054,0,1 29 | 203524,21900054,2,1 30 | 1626188,21900054,1,0 31 | 1627936,21900054,3,1 32 | 1628415,21900054,1,0 33 | 1628960,21900054,2,0 34 | 1628991,21900054,1,0 35 | 1629001,21900054,1,0 36 | 1629630,21900054,2,0 37 | 1629634,21900054,1,1 38 | 1629741,21900054,0,1 39 | 201144,21900063,2,0 40 | 201145,21900063,3,1 41 | 201976,21900063,1,0 42 | 202334,21900063,0,1 43 | 203210,21900063,0,1 44 | 203497,21900063,1,2 45 | 204060,21900063,2,0 46 | 1626144,21900063,1,1 47 | 1626149,21900063,0,1 48 | 1626220,21900063,1,0 49 | 1627826,21900063,0,2 50 | 1628378,21900063,2,0 51 | 1629013,21900063,1,0 52 | 1629611,21900063,1,0 53 | 201143,21900110,2,1 54 | 201144,21900110,2,1 55 | 202711,21900110,1,1 56 | 203497,21900110,3,1 57 | 203526,21900110,3,0 58 | 203954,21900110,0,2 59 | 1626196,21900110,3,0 60 | 1626220,21900110,1,1 61 | 1627732,21900110,1,0 62 | 1628378,21900110,1,0 63 | 1628396,21900110,1,0 64 | 201571,21900133,1,0 65 | 202696,21900133,1,2 66 | 203082,21900133,1,0 67 | 203095,21900133,0,1 68 | 203200,21900133,1,0 69 | 203487,21900133,0,1 70 | 203932,21900133,0,1 71 | 203933,21900133,1,0 72 | 203960,21900133,1,0 73 | 204456,21900133,0,1 74 | 1627734,21900133,1,1 75 | 1627763,21900133,1,0 76 | 1628365,21900133,1,0 77 | 1628371,21900133,0,2 78 | 1628388,21900133,0,1 79 | 1628964,21900133,1,4 80 | 1628988,21900133,1,0 81 | 101139,21900185,1,0 82 | 202397,21900185,1,1 83 | 202696,21900185,0,1 84 | 202722,21900185,2,0 85 | 202738,21900185,2,0 86 | 203078,21900185,1,0 87 | 203082,21900185,2,0 88 | 203095,21900185,1,0 89 | 1628365,21900185,1,0 90 | 1628411,21900185,1,1 91 | 1628418,21900185,0,1 92 | 1628964,21900185,0,1 93 | 202684,21900189,0,1 94 | 202694,21900189,1,2 95 | 203944,21900189,2,1 96 | 1626171,21900189,1,1 97 | 1628373,21900189,1,0 98 | 1628995,21900189,1,0 99 | 1629011,21900189,0,4 100 | 1629628,21900189,2,2 101 | 1629645,21900189,1,0 102 | 1629731,21900189,1,0 103 | 201144,21900210,1,0 104 | 202711,21900210,1,0 105 | 203476,21900210,1,3 106 | 203496,21900210,2,3 107 | 203497,21900210,0,5 108 | 204060,21900210,2,0 109 | 1626144,21900210,1,0 110 | 1626157,21900210,0,1 111 | 1626220,21900210,2,0 112 | 1628378,21900210,0,1 113 | 1629006,21900210,1,1 114 | 1629103,21900210,0,1 115 | 1629633,21900210,1,0 116 | 201565,21900241,0,1 117 | 202693,21900241,1,1 118 | 203083,21900241,0,1 119 | 203095,21900241,2,0 120 | 204038,21900241,1,0 121 | 1626174,21900241,1,1 122 | 1628365,21900241,3,0 123 | 1628371,21900241,1,4 124 | 1628379,21900241,1,1 125 | 1628964,21900241,2,3 126 | 1628971,21900241,1,0 127 | 1629004,21900241,1,0 128 | 201571,21900269,2,0 129 | 202329,21900269,2,0 130 | 203082,21900269,0,2 131 | 203095,21900269,2,0 132 | 203920,21900269,1,3 133 | 203932,21900269,2,1 134 | 1626178,21900269,1,0 135 | 1626181,21900269,2,1 136 | 1627783,21900269,1,1 137 | 1627832,21900269,7,0 138 | 1628365,21900269,1,1 139 | 1628371,21900269,0,2 140 | 1628384,21900269,3,0 141 | 1628449,21900269,1,2 142 | 1628964,21900269,0,2 143 | 1629056,21900269,1,0 144 | 101150,21900276,1,0 145 | 200746,21900276,1,4 146 | 200752,21900276,1,0 147 | 201942,21900276,3,0 148 | 201988,21900276,1,0 149 | 202695,21900276,2,1 150 | 203090,21900276,3,2 151 | 1626149,21900276,0,1 152 | 1626168,21900276,1,0 153 | 1627749,21900276,1,0 154 | 1627751,21900276,0,4 155 | 1629611,21900276,0,1 156 | 203476,21900286,1,2 157 | 203496,21900286,1,1 158 | 203524,21900286,1,0 159 | 203894,21900286,1,0 160 | 203952,21900286,1,2 161 | 203998,21900286,3,0 162 | 1626145,21900286,1,1 163 | 1628960,21900286,1,0 164 | 1628966,21900286,1,0 165 | 1628991,21900286,0,1 166 | 1629001,21900286,2,1 167 | 1629006,21900286,1,1 168 | 1629633,21900286,1,0 169 | 1713,21900295,1,1 170 | 202323,21900295,1,0 171 | 203458,21900295,2,0 172 | 203922,21900295,0,1 173 | 203953,21900295,2,1 174 | 1626161,21900295,2,2 175 | 1626172,21900295,0,2 176 | 1627737,21900295,1,0 177 | 1627745,21900295,1,0 178 | 1627761,21900295,3,1 179 | 1627820,21900295,1,0 180 | 1629016,21900295,2,1 181 | 1629065,21900295,3,1 182 | 1629629,21900295,1,1 183 | 1629631,21900295,1,0 184 | 1629673,21900295,3,1 185 | 201571,21900308,1,0 186 | 201937,21900308,1,0 187 | 203082,21900308,2,0 188 | 203095,21900308,1,0 189 | 203920,21900308,1,0 190 | 203932,21900308,1,0 191 | 1626162,21900308,2,0 192 | 1626163,21900308,1,0 193 | 1627767,21900308,2,0 194 | 1628365,21900308,0,1 195 | 1628371,21900308,2,1 196 | 1628411,21900308,0,1 197 | 1628964,21900308,1,4 198 | 1628975,21900308,1,0 199 | 1629660,21900308,2,0 200 | 1629661,21900308,1,0 201 | 2544,21900329,0,1 202 | 2546,21900329,3,0 203 | 2730,21900329,2,0 204 | 200765,21900329,2,1 205 | 201229,21900329,1,0 206 | 201580,21900329,0,2 207 | 201980,21900329,1,1 208 | 202355,21900329,3,1 209 | 203076,21900329,2,3 210 | 203468,21900329,1,0 211 | 203484,21900329,1,0 212 | 203584,21900329,1,0 213 | 203918,21900329,1,0 214 | 1627746,21900329,0,1 215 | 1627936,21900329,1,0 216 | 1629642,21900329,0,2 217 | 201143,21900332,3,1 218 | 203124,21900332,0,4 219 | 203504,21900332,4,0 220 | 203516,21900332,2,0 221 | 203521,21900332,1,0 222 | 203526,21900332,2,0 223 | 1627732,21900332,1,2 224 | 1627788,21900332,1,0 225 | 1627790,21900332,1,0 226 | 1629012,21900332,1,0 227 | 1629645,21900332,1,0 228 | 1629680,21900332,0,1 229 | 101150,21900343,0,1 230 | 201976,21900343,3,0 231 | 202331,21900343,2,1 232 | 202335,21900343,1,0 233 | 203087,21900343,1,0 234 | 203090,21900343,0,1 235 | 203200,21900343,1,0 236 | 203933,21900343,1,0 237 | 204456,21900343,0,1 238 | 1626149,21900343,0,3 239 | 1626167,21900343,0,1 240 | 1627734,21900343,1,1 241 | 1627763,21900343,2,0 242 | 1627826,21900343,0,1 243 | 1628476,21900343,1,0 244 | 1628988,21900343,1,0 245 | 101133,21900352,1,2 246 | 202397,21900352,0,1 247 | 202722,21900352,0,1 248 | 203078,21900352,0,1 249 | 203469,21900352,1,0 250 | 1626179,21900352,1,0 251 | 1628970,21900352,0,2 252 | 1628972,21900352,3,0 253 | 1628984,21900352,2,1 254 | 1629021,21900352,0,1 255 | 1629023,21900352,0,1 256 | 1629060,21900352,1,0 257 | 1629067,21900352,1,1 258 | 1629185,21900352,1,0 259 | 200746,21900396,1,2 260 | 200752,21900396,1,1 261 | 200782,21900396,2,0 262 | 201566,21900396,1,1 263 | 201935,21900396,1,1 264 | 203085,21900396,1,0 265 | 203463,21900396,1,0 266 | 203991,21900396,1,3 267 | 1627749,21900396,2,1 268 | 1627854,21900396,1,0 269 | 1627863,21900396,2,0 270 | 1629109,21900396,0,1 271 | 202696,21900414,0,1 272 | 203095,21900414,1,1 273 | 203115,21900414,2,0 274 | 203487,21900414,4,0 275 | 203914,21900414,1,1 276 | 203932,21900414,1,1 277 | 203999,21900414,1,1 278 | 1627750,21900414,1,0 279 | 1628365,21900414,1,1 280 | 1628420,21900414,2,0 281 | 1628964,21900414,2,1 282 | 1629008,21900414,1,1 283 | 2546,21900428,0,3 284 | 201229,21900428,1,0 285 | 202355,21900428,0,5 286 | 202696,21900428,6,2 287 | 203081,21900428,1,0 288 | 203082,21900428,1,0 289 | 203095,21900428,1,0 290 | 203145,21900428,1,1 291 | 203468,21900428,1,0 292 | 203932,21900428,0,1 293 | 1627746,21900428,0,2 294 | 1628365,21900428,1,0 295 | 1628371,21900428,4,2 296 | 1628964,21900428,1,0 297 | 200794,21900459,2,0 298 | 201950,21900459,6,0 299 | 202324,21900459,1,3 300 | 202734,21900459,0,1 301 | 203115,21900459,0,1 302 | 203486,21900459,0,1 303 | 203914,21900459,4,0 304 | 203999,21900459,2,0 305 | 1627736,21900459,0,1 306 | 1627742,21900459,2,1 307 | 1627750,21900459,1,0 308 | 1628420,21900459,1,1 309 | 101108,21900503,1,0 310 | 201568,21900503,1,0 311 | 203457,21900503,1,2 312 | 203500,21900503,1,0 313 | 203552,21900503,1,1 314 | 203939,21900503,2,1 315 | 1626153,21900503,0,1 316 | 1627827,21900503,0,1 317 | 1627846,21900503,0,1 318 | 1628382,21900503,1,0 319 | 1628977,21900503,1,0 320 | 1628983,21900503,2,1 321 | 1629029,21900503,1,0 322 | 1629647,21900503,0,1 323 | 101133,21900504,0,2 324 | 201571,21900504,2,0 325 | 202397,21900504,1,0 326 | 202696,21900504,1,0 327 | 202738,21900504,1,0 328 | 203082,21900504,1,1 329 | 203095,21900504,2,1 330 | 203895,21900504,1,0 331 | 203920,21900504,0,1 332 | 1628365,21900504,2,0 333 | 1628371,21900504,1,0 334 | 1628411,21900504,1,0 335 | 1628518,21900504,0,1 336 | 1628964,21900504,0,1 337 | 1628972,21900504,1,0 338 | 1628982,21900504,1,1 339 | 1629067,21900504,1,1 340 | 1629140,21900504,0,1 341 | 200768,21900510,0,1 342 | 201586,21900510,0,2 343 | 201609,21900510,0,1 344 | 202710,21900510,2,0 345 | 1626178,21900510,1,1 346 | 1627832,21900510,1,0 347 | 1627884,21900510,2,1 348 | 1628384,21900510,2,0 349 | 1628389,21900510,0,1 350 | 1628449,21900510,0,2 351 | 1629134,21900510,1,0 352 | 1629639,21900510,1,0 353 | 1629735,21900510,0,1 354 | 201565,21900532,1,0 355 | 202692,21900532,1,0 356 | 203083,21900532,3,2 357 | 203110,21900532,0,1 358 | 203922,21900532,1,0 359 | 204025,21900532,1,1 360 | 204038,21900532,2,0 361 | 1626174,21900532,1,1 362 | 1627737,21900532,2,0 363 | 1627814,21900532,1,1 364 | 1628971,21900532,2,0 365 | 1628980,21900532,1,0 366 | 1629004,21900532,1,0 367 | 1629016,21900532,4,1 368 | 1629635,21900532,2,1 369 | 1629672,21900532,1,0 370 | 1629673,21900532,2,0 371 | 201959,21900534,1,0 372 | 201976,21900534,1,0 373 | 202331,21900534,2,0 374 | 202694,21900534,2,1 375 | 203090,21900534,0,2 376 | 203493,21900534,3,0 377 | 203585,21900534,0,1 378 | 203944,21900534,2,0 379 | 1628443,21900534,1,0 380 | 1628995,21900534,0,1 381 | 1629011,21900534,1,1 382 | 1629013,21900534,1,0 383 | 1629628,21900534,1,1 384 | 200746,21900546,0,1 385 | 201572,21900546,0,1 386 | 201577,21900546,0,1 387 | 201988,21900546,1,0 388 | 202083,21900546,1,1 389 | 202339,21900546,0,1 390 | 203507,21900546,3,1 391 | 1626168,21900546,1,0 392 | 1627749,21900546,1,0 393 | 1627854,21900546,1,0 394 | 1628401,21900546,0,1 395 | 1628978,21900546,1,0 396 | 201565,21900548,1,0 397 | 201567,21900548,1,0 398 | 202684,21900548,0,2 399 | 202688,21900548,0,1 400 | 203083,21900548,1,2 401 | 203521,21900548,1,0 402 | 1626224,21900548,1,0 403 | 1627790,21900548,1,1 404 | 1628971,21900548,2,1 405 | 1629004,21900548,1,0 406 | 1629012,21900548,1,0 407 | 1629635,21900548,3,0 408 | 1629636,21900548,2,0 409 | 202334,21900561,0,1 410 | 202711,21900561,0,1 411 | 203497,21900561,1,1 412 | 203903,21900561,1,1 413 | 1626144,21900561,1,1 414 | 1626220,21900561,1,0 415 | 1627777,21900561,1,1 416 | 1628373,21900561,1,0 417 | 1628378,21900561,1,0 418 | 1628422,21900561,1,0 419 | 1629011,21900561,0,1 420 | 1629628,21900561,1,0 421 | 202330,21900578,1,1 422 | 202689,21900578,1,0 423 | 202734,21900578,1,0 424 | 202954,21900578,1,1 425 | 1626143,21900578,0,1 426 | 1627742,21900578,1,1 427 | 1627759,21900578,3,0 428 | 1628366,21900578,0,1 429 | 1628369,21900578,3,0 430 | 1628402,21900578,0,1 431 | 1628464,21900578,2,1 432 | 1629035,21900578,1,0 433 | 1629637,21900578,0,2 434 | 1629740,21900578,2,0 435 | 1629750,21900578,2,0 436 | 201949,21900584,0,1 437 | 201959,21900584,1,1 438 | 202710,21900584,0,1 439 | 203493,21900584,2,0 440 | 203901,21900584,2,0 441 | 203944,21900584,2,0 442 | 1627884,21900584,0,2 443 | 1628422,21900584,1,0 444 | 1628443,21900584,1,1 445 | 1629011,21900584,0,1 446 | 1629134,21900584,1,1 447 | 1629628,21900584,2,0 448 | 2772,21900612,1,0 449 | 202357,21900612,2,0 450 | 203084,21900612,1,0 451 | 203501,21900612,1,0 452 | 1626153,21900612,0,1 453 | 1627827,21900612,1,0 454 | 1628368,21900612,1,0 455 | 1628467,21900612,1,1 456 | 1628973,21900612,1,0 457 | 201609,21900644,1,0 458 | 201949,21900644,0,2 459 | 202357,21900644,1,0 460 | 203084,21900644,1,0 461 | 203086,21900644,3,0 462 | 203482,21900644,1,0 463 | 203992,21900644,1,1 464 | 1627741,21900644,3,2 465 | 1627812,21900644,1,0 466 | 1628368,21900644,3,0 467 | 1628389,21900644,1,1 468 | 1628963,21900644,1,0 469 | 1629639,21900644,3,0 470 | 200757,21900685,0,1 471 | 200782,21900685,2,2 472 | 201566,21900685,2,1 473 | 201569,21900685,1,1 474 | 203115,21900685,1,0 475 | 203914,21900685,2,0 476 | 203924,21900685,0,1 477 | 203991,21900685,0,1 478 | 203999,21900685,2,0 479 | 1627736,21900685,3,0 480 | 1627863,21900685,1,1 481 | 1628420,21900685,1,1 482 | 1628470,21900685,1,1 483 | 201959,21900688,0,2 484 | 202066,21900688,1,1 485 | 202694,21900688,1,1 486 | 203493,21900688,1,0 487 | 203901,21900688,1,2 488 | 203915,21900688,0,1 489 | 203925,21900688,2,0 490 | 203944,21900688,0,1 491 | 1626171,21900688,2,0 492 | 1627747,21900688,1,0 493 | 1628373,21900688,1,0 494 | 1628386,21900688,0,2 495 | 1628422,21900688,1,0 496 | 1629011,21900688,0,2 497 | 1629066,21900688,2,0 498 | 201143,21900702,1,0 499 | 202692,21900702,1,0 500 | 203110,21900702,1,3 501 | 203526,21900702,1,0 502 | 203922,21900702,1,0 503 | 203954,21900702,0,1 504 | 1626156,21900702,2,0 505 | 1627732,21900702,1,0 506 | 1627788,21900702,0,2 507 | 1628980,21900702,1,0 508 | 1629003,21900702,1,0 509 | 1629015,21900702,1,0 510 | 201572,21900739,0,8 511 | 201577,21900739,1,0 512 | 202339,21900739,0,1 513 | 203114,21900739,0,1 514 | 203507,21900739,1,1 515 | 1626162,21900739,1,0 516 | 1626192,21900739,0,1 517 | 1628969,21900739,2,2 518 | 1628975,21900739,3,0 519 | 1628978,21900739,2,0 520 | 1629028,21900739,1,1 521 | 1629059,21900739,1,0 522 | 200768,21900758,2,0 523 | 201586,21900758,1,0 524 | 203087,21900758,1,0 525 | 203200,21900758,3,1 526 | 203506,21900758,1,0 527 | 204456,21900758,0,1 528 | 1626167,21900758,1,2 529 | 1626178,21900758,1,0 530 | 1627783,21900758,3,0 531 | 1627832,21900758,3,0 532 | 1628384,21900758,1,0 533 | 1628988,21900758,0,1 534 | 1629056,21900758,1,0 535 | 201152,21900765,3,0 536 | 201950,21900765,0,2 537 | 202324,21900765,0,1 538 | 203107,21900765,1,0 539 | 203897,21900765,1,1 540 | 1626245,21900765,1,0 541 | 1627742,21900765,2,0 542 | 1627853,21900765,2,0 543 | 1627885,21900765,1,0 544 | 1628366,21900765,3,1 545 | 1628402,21900765,0,1 546 | 1628404,21900765,2,0 547 | 1628990,21900765,2,0 548 | 1629627,21900765,2,1 549 | 1629637,21900765,1,1 550 | 1629740,21900765,2,0 551 | 201565,21900808,1,1 552 | 202693,21900808,0,1 553 | 202696,21900808,3,1 554 | 203082,21900808,4,1 555 | 203089,21900808,0,1 556 | 203095,21900808,2,0 557 | 203487,21900808,1,2 558 | 203503,21900808,0,1 559 | 203920,21900808,0,1 560 | 203932,21900808,1,1 561 | 1626174,21900808,0,2 562 | 1627748,21900808,1,1 563 | 1628365,21900808,2,0 564 | 1629004,21900808,1,0 565 | 1629109,21900808,0,1 566 | 1629635,21900808,2,0 567 | 201937,21900855,7,0 568 | 202711,21900855,2,0 569 | 203497,21900855,1,1 570 | 203903,21900855,2,0 571 | 1626162,21900855,1,1 572 | 1626220,21900855,2,0 573 | 1628378,21900855,1,0 574 | 1628396,21900855,0,1 575 | 1628975,21900855,1,0 576 | 1629661,21900855,1,0 577 | 2544,21900861,0,1 578 | 2730,21900861,0,2 579 | 200765,21900861,1,0 580 | 201950,21900861,2,0 581 | 201980,21900861,2,1 582 | 202324,21900861,1,1 583 | 202340,21900861,1,0 584 | 202693,21900861,0,1 585 | 202734,21900861,1,0 586 | 203076,21900861,1,6 587 | 203484,21900861,2,0 588 | 1627742,21900861,2,0 589 | 1627936,21900861,2,1 590 | 1628398,21900861,1,0 591 | 1628404,21900861,3,0 592 | 1629627,21900861,1,0 593 | 1629740,21900861,0,1 594 | 201952,21900867,1,1 595 | 202696,21900867,0,1 596 | 203095,21900867,3,0 597 | 203487,21900867,0,1 598 | 203932,21900867,1,3 599 | 1628365,21900867,3,0 600 | 1628381,21900867,0,2 601 | 1628964,21900867,0,4 602 | 1628981,21900867,1,0 603 | 1629027,21900867,1,1 604 | 1629629,21900867,2,1 605 | 201567,21900890,1,0 606 | 203083,21900890,4,1 607 | 203200,21900890,3,0 608 | 203506,21900890,3,0 609 | 203933,21900890,1,0 610 | 1626167,21900890,0,4 611 | 1626204,21900890,3,0 612 | 1627734,21900890,2,1 613 | 1627763,21900890,1,0 614 | 1628988,21900890,0,1 615 | 1629645,21900890,1,2 616 | 202397,21900901,0,1 617 | 202722,21900901,0,1 618 | 203078,21900901,5,0 619 | 203894,21900901,3,0 620 | 203952,21900901,2,2 621 | 1627737,21900901,0,2 622 | 1627814,21900901,2,1 623 | 1628418,21900901,1,2 624 | 1628539,21900901,1,1 625 | 1628972,21900901,1,0 626 | 1629067,21900901,0,1 627 | 1629308,21900901,2,1 628 | 203115,21900913,1,0 629 | 203914,21900913,3,1 630 | 203924,21900913,0,1 631 | 203952,21900913,1,1 632 | 203999,21900913,1,2 633 | 1627737,21900913,2,1 634 | 1627750,21900913,1,0 635 | 1627814,21900913,3,0 636 | 1628420,21900913,0,1 637 | 1629308,21900913,0,1 638 | 1629672,21900913,1,0 639 | 1629673,21900913,1,0 640 | 202711,21900921,0,1 641 | 203090,21900921,1,0 642 | 203497,21900921,0,2 643 | 203903,21900921,2,0 644 | 204060,21900921,1,0 645 | 1626171,21900921,1,0 646 | 1628378,21900921,2,0 647 | 1628396,21900921,0,2 648 | 1628995,21900921,0,1 649 | 1629628,21900921,1,0 650 | 201959,21900969,2,3 651 | 203090,21900969,1,0 652 | 203473,21900969,1,0 653 | 203901,21900969,4,1 654 | 203944,21900969,1,1 655 | 1626171,21900969,0,1 656 | 1628373,21900969,1,0 657 | 1628381,21900969,2,1 658 | 1628989,21900969,1,1 659 | 1628995,21900969,2,0 660 | 1629011,21900969,1,2 661 | 1629628,21900969,2,1 662 | 1629629,21900969,2,1 663 | 201144,21901231,1,1 664 | 201950,21901231,3,1 665 | 202324,21901231,3,0 666 | 202734,21901231,1,0 667 | 203497,21901231,0,3 668 | 203903,21901231,2,0 669 | 204060,21901231,2,0 670 | 1626220,21901231,2,0 671 | 1627742,21901231,0,1 672 | 1628366,21901231,2,0 673 | 1628378,21901231,3,1 674 | 1628396,21901231,1,1 675 | 1629740,21901231,3,0 676 | 2544,21901232,1,1 677 | 2730,21901232,1,0 678 | 201149,21901232,0,1 679 | 201580,21901232,2,0 680 | 202331,21901232,3,0 681 | 202695,21901232,2,2 682 | 203079,21901232,0,1 683 | 203210,21901232,0,1 684 | 203484,21901232,1,0 685 | 1627826,21901232,1,1 686 | 1627936,21901232,1,0 687 | 1628398,21901232,0,1 688 | 203082,21901233,1,0 689 | 203095,21901233,0,1 690 | 203487,21901233,1,0 691 | 203516,21901233,1,0 692 | 203920,21901233,1,2 693 | 203925,21901233,1,0 694 | 1626147,21901233,0,1 695 | 1627747,21901233,1,0 696 | 1627789,21901233,1,0 697 | 1628365,21901233,1,0 698 | 1628371,21901233,1,0 699 | 1628964,21901233,1,0 700 | 1628982,21901233,1,0 701 | 1629725,21901233,1,0 702 | 1629743,21901233,0,1 703 | 201937,21901235,3,0 704 | 203894,21901235,1,1 705 | 203967,21901235,1,0 706 | 1626164,21901235,1,0 707 | 1626166,21901235,2,1 708 | 1628418,21901235,2,0 709 | 1628969,21901235,1,0 710 | 1628972,21901235,1,0 711 | 1628975,21901235,1,0 712 | 1629010,21901235,1,0 713 | 1629028,21901235,0,2 714 | 1629060,21901235,1,0 715 | 1629067,21901235,1,0 716 | 1629140,21901235,0,1 717 | 1629661,21901235,1,0 718 | 2544,21901243,0,1 719 | 2730,21901243,0,1 720 | 2747,21901243,1,0 721 | 200768,21901243,1,0 722 | 201580,21901243,0,1 723 | 201586,21901243,0,2 724 | 201980,21901243,1,2 725 | 202693,21901243,2,1 726 | 203076,21901243,1,3 727 | 203079,21901243,0,1 728 | 1626181,21901243,1,0 729 | 1627783,21901243,2,2 730 | 1627832,21901243,1,1 731 | 1627936,21901243,3,1 732 | 1628384,21901243,2,0 733 | 1628398,21901243,0,1 734 | 2546,21901245,0,1 735 | 202330,21901245,1,0 736 | 202683,21901245,0,1 737 | 202689,21901245,0,1 738 | 203081,21901245,0,1 739 | 203468,21901245,2,1 740 | 203935,21901245,3,2 741 | 203994,21901245,2,1 742 | 1626209,21901245,2,0 743 | 1627759,21901245,2,0 744 | 1628369,21901245,2,1 745 | 1628380,21901245,1,0 746 | 1628464,21901245,1,1 747 | 1629018,21901245,1,1 748 | 201147,21901247,3,0 749 | 202696,21901247,2,0 750 | 202709,21901247,1,0 751 | 203082,21901247,1,0 752 | 203095,21901247,1,0 753 | 203487,21901247,3,2 754 | 203516,21901247,1,0 755 | 203953,21901247,1,0 756 | 203992,21901247,1,0 757 | 1627741,21901247,1,0 758 | 1627812,21901247,1,0 759 | 1628368,21901247,1,0 760 | 1628371,21901247,2,0 761 | 1628964,21901247,0,1 762 | 1629109,21901247,0,1 763 | 1629610,21901247,1,0 764 | 203501,21901249,1,0 765 | 203504,21901249,2,0 766 | 204001,21901249,1,4 767 | 1626153,21901249,2,1 768 | 1626164,21901249,1,0 769 | 1626166,21901249,1,1 770 | 1627827,21901249,1,0 771 | 1628467,21901249,1,0 772 | 1628969,21901249,2,3 773 | 1628975,21901249,1,0 774 | 200768,21901250,1,2 775 | 201188,21901250,1,0 776 | 201609,21901250,2,1 777 | 202710,21901250,2,2 778 | 203109,21901250,1,0 779 | 203482,21901250,1,1 780 | 1627783,21901250,1,0 781 | 1627832,21901250,1,0 782 | 1628384,21901250,1,1 783 | 1628389,21901250,1,1 784 | 1629056,21901250,1,0 785 | 1629130,21901250,0,1 786 | 1629134,21901250,1,2 787 | 1629639,21901250,1,0 788 | 203200,21901252,0,1 789 | 203894,21901252,0,1 790 | 203933,21901252,3,4 791 | 203960,21901252,1,2 792 | 1626167,21901252,0,2 793 | 1627763,21901252,1,1 794 | 1628418,21901252,0,3 795 | 1628988,21901252,1,1 796 | 1629010,21901252,1,1 797 | 1629021,21901252,2,2 798 | 1629067,21901252,3,0 799 | 201950,21901253,1,0 800 | 202324,21901253,0,1 801 | 202685,21901253,1,4 802 | 203937,21901253,0,2 803 | 1627742,21901253,2,0 804 | 1628366,21901253,4,0 805 | 1628960,21901253,1,0 806 | 1628991,21901253,1,2 807 | 1629001,21901253,0,1 808 | 1629634,21901253,0,1 809 | 201143,21901254,0,2 810 | 201942,21901254,1,2 811 | 201988,21901254,1,0 812 | 202699,21901254,1,1 813 | 203954,21901254,1,0 814 | 1626196,21901254,1,1 815 | 1627732,21901254,1,1 816 | 1627749,21901254,2,0 817 | 1627751,21901254,1,1 818 | 1627788,21901254,1,0 819 | 1628401,21901254,1,0 820 | 1629234,21901254,0,2 821 | 1629680,21901254,1,1 822 | 1629683,21901254,1,1 823 | 2544,21901255,2,1 824 | 2730,21901255,1,1 825 | 201980,21901255,2,0 826 | 203076,21901255,3,1 827 | 203079,21901255,2,0 828 | 203484,21901255,2,0 829 | 203497,21901255,0,1 830 | 204060,21901255,1,1 831 | 1626220,21901255,4,0 832 | 1627777,21901255,3,0 833 | 1628396,21901255,0,1 834 | 101107,21901256,0,2 835 | 101141,21901256,0,1 836 | 201577,21901256,0,2 837 | 201588,21901256,1,0 838 | 202066,21901256,0,2 839 | 204020,21901256,1,0 840 | 1626147,21901256,0,1 841 | 1626192,21901256,2,0 842 | 1629066,21901256,2,0 843 | 1629725,21901256,1,1 844 | 1629743,21901256,1,2 845 | 202694,21901258,0,2 846 | 202695,21901258,1,0 847 | 1626166,21901258,1,0 848 | 1627826,21901258,0,1 849 | 1628969,21901258,2,0 850 | 1628975,21901258,1,0 851 | 1629013,21901258,1,0 852 | 1629661,21901258,1,0 853 | 2738,21901260,1,2 854 | 201609,21901260,2,0 855 | 202954,21901260,1,0 856 | 203482,21901260,2,1 857 | 203935,21901260,2,0 858 | 1627759,21901260,2,1 859 | 1628369,21901260,1,1 860 | 1628389,21901260,1,1 861 | 1628400,21901260,1,0 862 | 1628464,21901260,0,1 863 | 1629130,21901260,0,1 864 | 1629134,21901260,1,0 865 | 1629639,21901260,1,0 866 | 1629684,21901260,0,1 867 | 2546,21901261,2,1 868 | 200782,21901261,1,0 869 | 201566,21901261,2,1 870 | 202355,21901261,0,1 871 | 203081,21901261,2,0 872 | 203463,21901261,2,1 873 | 203496,21901261,2,0 874 | 203994,21901261,1,3 875 | 1627863,21901261,0,1 876 | 1628380,21901261,1,0 877 | 1629018,21901261,1,0 878 | 201144,21901262,1,0 879 | 201229,21901262,1,0 880 | 203476,21901262,0,1 881 | 203497,21901262,2,3 882 | 203903,21901262,1,0 883 | 203937,21901262,1,1 884 | 1626220,21901262,1,1 885 | 1628415,21901262,1,0 886 | 1629630,21901262,2,0 887 | 1629634,21901262,2,1 888 | 202397,21901263,0,1 889 | 202692,21901263,1,0 890 | 202699,21901263,1,1 891 | 203894,21901263,1,0 892 | 203954,21901263,3,2 893 | 1626196,21901263,1,0 894 | 1627732,21901263,2,0 895 | 1628418,21901263,2,4 896 | 1629003,21901263,1,1 897 | 1629010,21901263,2,0 898 | 1629021,21901263,1,0 899 | 1629060,21901263,1,0 900 | 1629067,21901263,1,0 901 | 1629680,21901263,2,0 902 | 200752,21901264,1,2 903 | 200794,21901264,1,1 904 | 201942,21901264,1,2 905 | 203584,21901264,1,0 906 | 203924,21901264,1,1 907 | 203999,21901264,1,0 908 | 1627749,21901264,3,0 909 | 1628401,21901264,2,1 910 | 1628408,21901264,1,0 911 | 1628420,21901264,1,0 912 | 1628470,21901264,0,2 913 | 1629008,21901264,0,1 914 | 1629234,21901264,1,0 915 | 1629640,21901264,1,0 916 | 1629683,21901264,1,0 917 | 2544,21901265,1,0 918 | 101108,21901265,1,0 919 | 201568,21901265,1,0 920 | 201580,21901265,0,1 921 | 201980,21901265,2,0 922 | 202693,21901265,1,1 923 | 203079,21901265,1,1 924 | 203457,21901265,2,1 925 | 1626188,21901265,1,0 926 | 1627936,21901265,1,0 927 | 1628977,21901265,1,0 928 | 1628983,21901265,3,1 929 | 1629652,21901265,1,0 930 | 101107,21901269,1,0 931 | 201572,21901269,2,2 932 | 201588,21901269,1,0 933 | 202339,21901269,2,0 934 | 203109,21901269,1,0 935 | 203482,21901269,1,0 936 | 203507,21901269,0,1 937 | 203524,21901269,2,0 938 | 1628389,21901269,1,0 939 | 1628978,21901269,1,0 940 | 1629134,21901269,0,1 941 | 1629639,21901269,1,0 942 | 203200,21901270,3,0 943 | 203933,21901270,2,0 944 | 1626166,21901270,1,0 945 | 1626167,21901270,1,3 946 | 1627763,21901270,0,1 947 | 1628969,21901270,1,0 948 | 1628975,21901270,1,0 949 | 1629028,21901270,2,4 950 | 1629048,21901270,0,1 951 | 1629661,21901270,1,0 952 | 202694,21901271,2,0 953 | 202695,21901271,1,0 954 | 203210,21901271,0,1 955 | 204001,21901271,0,1 956 | 1626153,21901271,1,0 957 | 1626246,21901271,0,1 958 | 1627826,21901271,0,1 959 | 1627827,21901271,0,1 960 | 1628382,21901271,1,0 961 | 1628467,21901271,1,1 962 | 1629013,21901271,0,1 963 | 202355,21901272,0,2 964 | 203081,21901272,3,0 965 | 203468,21901272,0,1 966 | 203486,21901272,1,0 967 | 203924,21901272,0,1 968 | 203994,21901272,1,2 969 | 1628380,21901272,1,1 970 | 1628470,21901272,1,1 971 | 1629008,21901272,2,1 972 | 1629018,21901272,2,0 973 | 1629626,21901272,1,1 974 | 2730,21901273,1,0 975 | 200782,21901273,2,0 976 | 201145,21901273,2,0 977 | 201935,21901273,3,1 978 | 201980,21901273,2,1 979 | 203076,21901273,0,1 980 | 203079,21901273,1,1 981 | 203463,21901273,1,1 982 | 203484,21901273,1,1 983 | 203496,21901273,3,2 984 | 1627863,21901273,0,1 985 | 1628398,21901273,2,2 986 | 1629659,21901273,4,0 987 | 200752,21901274,3,0 988 | 201942,21901274,2,0 989 | 1626144,21901274,1,0 990 | 1627751,21901274,0,3 991 | 1627777,21901274,2,0 992 | 1628396,21901274,1,3 993 | 1628401,21901274,1,1 994 | 1629022,21901274,1,0 995 | 1629625,21901274,1,0 996 | 1629640,21901274,3,1 997 | 1629671,21901274,1,0 998 | 1629714,21901274,2,1 999 | 201950,21901278,2,1 1000 | 202324,21901278,2,0 1001 | 202397,21901278,4,0 1002 | 202734,21901278,1,1 1003 | 1626170,21901278,1,0 1004 | 1627742,21901278,1,0 1005 | 1628366,21901278,1,3 1006 | 1628418,21901278,2,2 1007 | 1628972,21901278,2,0 1008 | 1629010,21901278,0,2 1009 | 1629740,21901278,0,3 1010 | 2546,21901280,0,1 1011 | 101150,21901280,0,1 1012 | 202331,21901280,3,0 1013 | 202694,21901280,1,0 1014 | 203081,21901280,1,0 1015 | 203210,21901280,1,1 1016 | 203994,21901280,2,0 1017 | 1628380,21901280,0,1 1018 | 1629018,21901280,1,0 1019 | 1629117,21901280,1,0 1020 | 2544,21901282,0,1 1021 | 2730,21901282,1,1 1022 | 201162,21901282,2,0 1023 | 201580,21901282,2,0 1024 | 203076,21901282,2,2 1025 | 203200,21901282,1,1 1026 | 203506,21901282,1,0 1027 | 204456,21901282,4,0 1028 | 1626167,21901282,1,1 1029 | 1627763,21901282,1,0 1030 | 1627936,21901282,3,0 1031 | 1628398,21901282,3,1 1032 | 1628988,21901282,3,1 1033 | 1629048,21901282,1,1 1034 | 1629659,21901282,0,1 1035 | 101108,21901285,3,2 1036 | 201568,21901285,1,0 1037 | 202397,21901285,1,1 1038 | 1628418,21901285,0,2 1039 | 1628972,21901285,2,0 1040 | 1628977,21901285,1,0 1041 | 1628983,21901285,1,0 1042 | 1629010,21901285,2,0 1043 | 1629021,21901285,1,0 1044 | 1629647,21901285,1,0 1045 | 1629652,21901285,0,1 1046 | 200752,21901287,3,1 1047 | 200755,21901287,1,0 1048 | 201942,21901287,1,0 1049 | 201950,21901287,0,1 1050 | 202324,21901287,0,1 1051 | 202734,21901287,1,0 1052 | 1627742,21901287,1,0 1053 | 1627749,21901287,2,1 1054 | 1627751,21901287,2,0 1055 | 1628366,21901287,0,2 1056 | 1628401,21901287,0,1 1057 | 1629022,21901287,0,1 1058 | 1629234,21901287,2,2 1059 | 1629637,21901287,1,0 1060 | 1629638,21901287,1,0 1061 | 1629640,21901287,2,0 1062 | 1629740,21901287,0,1 1063 | 201571,21901288,1,0 1064 | 202330,21901288,0,1 1065 | 202954,21901288,1,0 1066 | 203082,21901288,1,0 1067 | 203935,21901288,3,0 1068 | 1627759,21901288,2,1 1069 | 1628365,21901288,2,1 1070 | 1628369,21901288,1,1 1071 | 1628400,21901288,1,0 1072 | 1628411,21901288,0,1 1073 | 1628464,21901288,0,3 1074 | 1628982,21901288,2,0 1075 | 1629057,21901288,1,1 1076 | 1629109,21901288,0,2 1077 | 1629724,21901288,1,0 1078 | 2546,21901289,2,1 1079 | 201143,21901289,0,1 1080 | 203081,21901289,2,0 1081 | 203468,21901289,1,0 1082 | 203658,21901289,1,1 1083 | 203922,21901289,1,0 1084 | 203994,21901289,0,3 1085 | 1626209,21901289,2,0 1086 | 1627788,21901289,1,0 1087 | 1628380,21901289,1,1 1088 | 1629117,21901289,0,1 1089 | 1629680,21901289,3,1 1090 | 101150,21901291,2,1 1091 | 202066,21901291,1,0 1092 | 202694,21901291,1,0 1093 | 202695,21901291,4,0 1094 | 203210,21901291,1,0 1095 | 1626147,21901291,0,2 1096 | 1627747,21901291,1,0 1097 | 1627826,21901291,0,1 1098 | 1628386,21901291,1,2 1099 | 1629066,21901291,1,0 1100 | 1629185,21901291,1,1 1101 | 101108,21901292,2,0 1102 | 201937,21901292,1,0 1103 | 203488,21901292,1,0 1104 | 203967,21901292,1,1 1105 | 1626164,21901292,0,2 1106 | 1626166,21901292,3,0 1107 | 1627767,21901292,1,0 1108 | 1627846,21901292,2,0 1109 | 1628969,21901292,1,0 1110 | 1628975,21901292,3,2 1111 | 1628977,21901292,2,0 1112 | 1628987,21901292,0,1 1113 | 1629647,21901292,1,1 1114 | 1629652,21901292,2,0 1115 | 1629660,21901292,1,0 1116 | 1629661,21901292,0,2 1117 | 201144,21901293,1,0 1118 | 202334,21901293,1,0 1119 | 203497,21901293,0,1 1120 | 203504,21901293,2,0 1121 | 204060,21901293,1,1 1122 | 1626144,21901293,1,0 1123 | 1626153,21901293,3,0 1124 | 1626220,21901293,1,0 1125 | 1626246,21901293,1,2 1126 | 1628396,21901293,0,1 1127 | 1628467,21901293,0,1 1128 | 1629671,21901293,0,1 1129 | 1629714,21901293,0,1 1130 | 1629730,21901293,0,1 1131 | 2738,21901295,1,2 1132 | 201609,21901295,1,0 1133 | 202710,21901295,4,1 1134 | 203109,21901295,2,1 1135 | 203200,21901295,1,0 1136 | 203482,21901295,1,0 1137 | 203506,21901295,1,0 1138 | 203960,21901295,0,1 1139 | 204456,21901295,2,0 1140 | 1626167,21901295,0,2 1141 | 1627884,21901295,1,1 1142 | 1628389,21901295,0,3 1143 | 1628988,21901295,1,1 1144 | 1629216,21901295,1,0 1145 | 1629639,21901295,0,1 1146 | 202696,21901297,1,0 1147 | 203516,21901297,1,1 1148 | 203920,21901297,2,0 1149 | 1626147,21901297,0,1 1150 | 1627789,21901297,1,0 1151 | 1628365,21901297,2,0 1152 | 1628982,21901297,1,0 1153 | 1629058,21901297,1,0 1154 | 1629066,21901297,1,1 1155 | 1629109,21901297,0,2 1156 | 1629168,21901297,1,0 1157 | 1629185,21901297,4,0 1158 | 1629724,21901297,1,0 1159 | 1629725,21901297,1,0 1160 | 1629743,21901297,1,2 1161 | 200752,21901298,1,0 1162 | 200782,21901298,1,0 1163 | 201158,21901298,1,0 1164 | 201566,21901298,1,0 1165 | 201601,21901298,2,0 1166 | 201942,21901298,2,1 1167 | 201960,21901298,2,1 1168 | 203085,21901298,1,0 1169 | 203496,21901298,4,2 1170 | 203998,21901298,1,0 1171 | 1627749,21901298,2,0 1172 | 1627751,21901298,1,1 1173 | 1629002,21901298,1,1 1174 | 1629022,21901298,2,0 1175 | 1629234,21901298,0,1 1176 | 1629598,21901298,2,0 1177 | 1629677,21901298,0,1 1178 | 201937,21901299,0,1 1179 | 202692,21901299,1,0 1180 | 203118,21901299,1,0 1181 | 203124,21901299,2,1 1182 | 203658,21901299,0,2 1183 | 1626163,21901299,1,0 1184 | 1626164,21901299,1,0 1185 | 1627788,21901299,1,0 1186 | 1628969,21901299,2,2 1187 | 1629680,21901299,0,1 1188 | 201588,21901303,1,0 1189 | 203507,21901303,1,0 1190 | 1626192,21901303,1,0 1191 | 1628394,21901303,1,0 1192 | 1628412,21901303,2,0 1193 | 1628418,21901303,0,1 1194 | 1628425,21901303,1,0 1195 | 1629067,21901303,1,1 1196 | 1629678,21901303,0,1 1197 | 200782,21901304,0,1 1198 | 201145,21901304,0,1 1199 | 201569,21901304,1,0 1200 | 201935,21901304,3,0 1201 | 201960,21901304,1,0 1202 | 203085,21901304,2,0 1203 | 203200,21901304,2,1 1204 | 203463,21901304,1,2 1205 | 203496,21901304,0,2 1206 | 203506,21901304,1,1 1207 | 203926,21901304,1,1 1208 | 203960,21901304,2,0 1209 | 204456,21901304,2,0 1210 | 1626167,21901304,1,1 1211 | 1628410,21901304,0,1 1212 | 1628988,21901304,1,0 1213 | 1628993,21901304,3,1 1214 | 1629048,21901304,2,1 1215 | 2738,21901306,2,1 1216 | 101108,21901306,1,0 1217 | 201609,21901306,2,0 1218 | 202710,21901306,2,0 1219 | 203086,21901306,1,0 1220 | 203500,21901306,1,0 1221 | 203524,21901306,2,0 1222 | 1627846,21901306,1,0 1223 | 1627884,21901306,1,0 1224 | 1628390,21901306,1,1 1225 | 1628977,21901306,1,0 1226 | 1628983,21901306,0,1 1227 | 1628985,21901306,1,1 1228 | 1629216,21901306,3,0 1229 | 1629639,21901306,1,1 1230 | 1629647,21901306,1,1 1231 | 2546,21901309,1,0 1232 | 202355,21901309,1,2 1233 | 203081,21901309,2,0 1234 | 203468,21901309,3,1 1235 | 203994,21901309,1,0 1236 | 204020,21901309,1,0 1237 | 1627747,21901309,2,1 1238 | 1628386,21901309,0,3 1239 | 1629018,21901309,2,1 1240 | 1629066,21901309,0,1 1241 | 1629725,21901309,2,0 1242 | 201229,21901311,1,0 1243 | 201572,21901311,0,1 1244 | 201577,21901311,0,1 1245 | 202339,21901311,2,0 1246 | 203648,21901311,1,0 1247 | 203937,21901311,2,1 1248 | 1626192,21901311,1,0 1249 | 1628391,21901311,1,0 1250 | 1628412,21901311,1,1 1251 | 1628415,21901311,0,1 1252 | 1628425,21901311,1,0 1253 | 1628978,21901311,1,0 1254 | 1629634,21901311,1,1 1255 | 201937,21901313,1,0 1256 | 203077,21901313,0,1 1257 | 203504,21901313,1,0 1258 | 1626153,21901313,1,0 1259 | 1628467,21901313,0,1 1260 | 1628499,21901313,0,2 1261 | 1628969,21901313,0,1 1262 | 1628975,21901313,1,1 1263 | 1629028,21901313,2,1 1264 | 1629029,21901313,1,0 1265 | 1629661,21901313,2,1 1266 | 204060,21901314,1,0 1267 | 1626220,21901314,0,2 1268 | 1627751,21901314,1,2 1269 | 1628396,21901314,0,1 1270 | 1628430,21901314,2,0 1271 | 1629002,21901314,1,1 1272 | 1629022,21901314,1,0 1273 | 1629234,21901314,0,4 1274 | 1629625,21901314,1,0 1275 | 1629640,21901314,1,0 1276 | 1629671,21901314,1,0 1277 | 1629677,21901314,0,1 1278 | 1629714,21901314,1,1 1279 | 1629752,21901314,1,1 1280 | 201149,21901317,1,0 1281 | 203210,21901317,1,0 1282 | 203457,21901317,0,1 1283 | 203460,21901317,0,1 1284 | 203471,21901317,1,0 1285 | 203488,21901317,0,1 1286 | 203585,21901317,2,0 1287 | 1627826,21901317,0,1 1288 | 1628977,21901317,0,1 1289 | 1628983,21901317,1,0 1290 | 1628985,21901317,3,0 1291 | 1629599,21901317,4,1 1292 | 1629611,21901317,2,0 1293 | 1629647,21901317,0,2 1294 | 200794,21901318,2,1 1295 | 201188,21901318,1,1 1296 | 203486,21901318,0,4 1297 | 203584,21901318,1,0 1298 | 1626169,21901318,1,1 1299 | 1626178,21901318,0,1 1300 | 1626181,21901318,1,1 1301 | 1626259,21901318,1,0 1302 | 1627750,21901318,1,0 1303 | 1628408,21901318,2,0 1304 | 1628420,21901318,0,1 1305 | 1628449,21901318,0,1 1306 | 1628778,21901318,1,0 1307 | 1628966,21901318,1,2 1308 | 1629056,21901318,2,0 1309 | 1629076,21901318,2,0 1310 | 1629626,21901318,0,3 1311 | 1629744,21901318,1,0 1312 | -------------------------------------------------------------------------------- /data/problems/combine2/center.csv: -------------------------------------------------------------------------------- 1 | player_id,game_id,pos,fgm,fga,pts,oreb,dreb,stl,blk 2 | 2730,21900002,Center,1,3,3,3,3,0,1 3 | 201580,21900002,Center,2,3,4,1,1,0,2 4 | 1627826,21900002,Center,4,4,8,1,0,0,0 5 | 201143,21900008,Center,5,13,16,1,1,1,0 6 | 202683,21900008,Center,5,8,12,3,3,0,0 7 | 203124,21900008,Center,2,2,5,0,1,0,0 8 | 203954,21900008,Center,5,14,15,4,9,0,3 9 | 1629057,21900008,Center,2,4,5,1,2,1,1 10 | 2730,21900054,Center,1,2,2,1,4,1,2 11 | 201580,21900054,Center,5,7,10,1,8,0,1 12 | 202685,21900054,Center,5,10,14,3,8,0,0 13 | 202334,21900063,Center,2,3,4,3,6,0,1 14 | 203497,21900063,Center,5,7,13,0,7,1,2 15 | 1627826,21900063,Center,4,4,8,2,6,0,2 16 | 201143,21900110,Center,3,14,7,1,3,2,1 17 | 203497,21900110,Center,4,8,14,4,12,3,1 18 | 203954,21900110,Center,5,16,27,4,12,0,2 19 | 1628396,21900110,Center,4,5,8,3,0,1,0 20 | 202696,21900133,Center,8,17,18,3,14,1,2 21 | 1628964,21900133,Center,2,7,5,2,1,1,4 22 | 202696,21900185,Center,11,14,30,7,10,0,1 23 | 1628418,21900185,Center,6,10,14,2,4,0,1 24 | 1628964,21900185,Center,1,4,2,2,4,0,1 25 | 202684,21900189,Center,3,6,9,3,2,0,1 26 | 1627790,21900189,Center,2,4,5,1,5,0,0 27 | 1629011,21900189,Center,3,7,7,4,4,0,4 28 | 203476,21900210,Center,0,3,1,1,7,1,3 29 | 203497,21900210,Center,5,8,12,3,12,0,5 30 | 1626157,21900210,Center,5,10,14,2,10,0,1 31 | 203083,21900241,Center,3,6,7,3,15,0,1 32 | 203920,21900241,Center,2,3,6,4,2,0,0 33 | 1628964,21900241,Center,3,8,6,4,8,2,3 34 | 201188,21900269,Center,1,6,6,1,6,0,0 35 | 203920,21900269,Center,2,4,4,1,11,1,3 36 | 1628964,21900269,Center,4,6,11,4,3,0,2 37 | 200746,21900276,Center,5,13,17,1,7,1,4 38 | 1627751,21900276,Center,3,4,6,2,4,0,4 39 | 1627826,21900276,Center,3,5,8,1,4,0,0 40 | 203476,21900286,Center,1,4,3,2,3,1,2 41 | 1626157,21900286,Center,7,20,21,3,9,0,0 42 | 203458,21900295,Center,5,7,11,0,4,2,0 43 | 1626161,21900295,Center,2,4,4,0,4,2,2 44 | 1627745,21900295,Center,6,8,16,4,4,1,0 45 | 203920,21900308,Center,3,4,7,4,5,1,0 46 | 1628964,21900308,Center,4,4,10,3,8,1,4 47 | 2730,21900329,Center,1,1,5,1,9,2,0 48 | 201580,21900329,Center,6,7,13,1,2,0,2 49 | 202355,21900329,Center,8,12,17,3,7,3,1 50 | 201143,21900332,Center,3,4,8,0,5,3,1 51 | 202684,21900332,Center,5,10,12,2,1,0,0 52 | 203124,21900332,Center,5,10,10,3,8,0,4 53 | 203658,21900332,Center,2,3,5,2,6,0,0 54 | 1627790,21900332,Center,2,3,5,1,3,1,0 55 | 1626167,21900343,Center,2,9,5,3,2,0,1 56 | 1627826,21900343,Center,5,7,13,3,5,0,1 57 | 101133,21900352,Center,3,5,9,1,2,1,2 58 | 202687,21900352,Center,4,6,13,5,10,0,0 59 | 200746,21900396,Center,7,14,19,2,10,1,2 60 | 203991,21900396,Center,7,9,15,3,12,1,3 61 | 1627751,21900396,Center,1,3,2,1,1,0,0 62 | 202696,21900414,Center,7,16,20,2,5,0,1 63 | 203999,21900414,Center,7,14,18,3,6,1,1 64 | 1628964,21900414,Center,4,9,9,2,3,2,1 65 | 202355,21900428,Center,3,9,10,3,14,0,5 66 | 202696,21900428,Center,9,20,23,4,8,6,2 67 | 203920,21900428,Center,0,1,0,0,0,0,0 68 | 1628964,21900428,Center,1,2,2,3,3,1,0 69 | 203999,21900459,Center,8,20,23,4,6,2,0 70 | 1626143,21900459,Center,0,1,0,2,1,0,0 71 | 1629637,21900459,Center,1,1,2,1,0,0,0 72 | 203457,21900503,Center,5,9,12,8,4,1,2 73 | 203500,21900503,Center,4,7,10,4,7,1,0 74 | 1626246,21900503,Center,2,4,4,2,4,0,0 75 | 101133,21900504,Center,1,2,2,0,2,0,2 76 | 202696,21900504,Center,8,20,20,2,10,1,0 77 | 203920,21900504,Center,3,3,7,4,6,0,1 78 | 1628394,21900504,Center,2,8,4,3,7,0,0 79 | 1628964,21900504,Center,3,6,7,0,6,0,1 80 | 1628389,21900510,Center,7,8,15,2,12,0,1 81 | 203083,21900532,Center,7,14,14,5,13,3,2 82 | 1626161,21900532,Center,2,5,5,1,2,0,0 83 | 1627826,21900534,Center,2,3,4,1,1,0,0 84 | 1629011,21900534,Center,4,5,8,0,2,1,1 85 | 200746,21900546,Center,5,10,17,1,3,0,1 86 | 201572,21900546,Center,3,6,8,0,3,0,1 87 | 201577,21900546,Center,1,6,2,4,3,0,1 88 | 1627751,21900546,Center,1,1,2,0,4,0,0 89 | 202684,21900548,Center,3,7,8,6,9,0,2 90 | 203083,21900548,Center,8,12,23,8,12,1,2 91 | 1627790,21900548,Center,2,3,5,1,8,1,1 92 | 202334,21900561,Center,0,1,1,2,0,0,1 93 | 203497,21900561,Center,5,5,16,3,13,1,1 94 | 1628396,21900561,Center,6,7,12,4,3,0,0 95 | 1629011,21900561,Center,3,4,6,1,3,0,1 96 | 202683,21900578,Center,10,13,22,7,12,0,0 97 | 1626143,21900578,Center,3,4,8,3,1,0,1 98 | 1629637,21900578,Center,9,13,20,1,1,0,2 99 | 1628389,21900584,Center,5,7,15,2,4,0,0 100 | 1629011,21900584,Center,1,1,2,0,3,0,1 101 | 203473,21900644,Center,2,5,6,2,6,0,0 102 | 1628389,21900644,Center,6,9,16,4,7,1,1 103 | 203991,21900685,Center,3,8,9,8,4,0,1 104 | 203999,21900685,Center,9,18,24,2,10,2,0 105 | 1628386,21900688,Center,2,3,5,2,3,0,2 106 | 1629011,21900688,Center,6,6,12,2,3,0,2 107 | 201143,21900702,Center,4,11,12,0,11,1,0 108 | 203954,21900702,Center,9,13,24,2,8,0,1 109 | 201572,21900739,Center,7,16,17,2,2,0,8 110 | 201577,21900739,Center,0,1,0,1,4,1,0 111 | 1629028,21900739,Center,10,27,20,8,6,1,1 112 | 1626167,21900758,Center,3,6,6,2,5,1,2 113 | 1626143,21900765,Center,1,2,2,0,0,0,0 114 | 1629637,21900765,Center,3,4,9,3,2,1,1 115 | 202696,21900808,Center,9,21,19,1,10,3,1 116 | 203920,21900808,Center,4,5,8,2,2,0,1 117 | 203382,21900855,Center,4,10,9,6,2,0,0 118 | 203497,21900855,Center,3,5,13,0,8,1,1 119 | 1628396,21900855,Center,1,1,4,1,0,0,1 120 | 1629028,21900855,Center,8,9,16,2,3,0,0 121 | 2730,21900861,Center,1,1,2,1,3,0,2 122 | 201580,21900861,Center,1,2,2,3,6,0,0 123 | 1626143,21900861,Center,0,0,0,0,2,0,0 124 | 202696,21900867,Center,8,15,17,1,11,0,1 125 | 1627745,21900867,Center,0,1,0,0,3,0,0 126 | 1628964,21900867,Center,4,7,15,6,4,0,4 127 | 203083,21900890,Center,12,21,27,1,12,4,1 128 | 1626167,21900890,Center,4,9,10,3,7,0,4 129 | 1629048,21900890,Center,0,1,0,0,0,0,0 130 | 1628394,21900901,Center,1,1,3,0,1,0,0 131 | 1628418,21900901,Center,5,6,12,1,1,1,2 132 | 203999,21900913,Center,7,15,16,4,9,1,2 133 | 203497,21900921,Center,7,8,18,4,10,0,2 134 | 1628396,21900921,Center,4,4,8,1,4,0,2 135 | 203473,21900969,Center,3,8,6,4,9,1,0 136 | 1629011,21900969,Center,7,7,16,4,2,1,2 137 | 203497,21901231,Center,5,9,14,2,10,0,3 138 | 1628396,21901231,Center,2,2,4,3,2,1,1 139 | 1629637,21901231,Center,1,2,3,5,2,0,0 140 | 2730,21901232,Center,0,0,1,2,1,1,0 141 | 201580,21901232,Center,3,4,6,4,3,2,0 142 | 1627826,21901232,Center,1,3,2,1,2,1,1 143 | 202696,21901233,Center,8,12,22,0,7,0,0 144 | 203920,21901233,Center,3,6,12,2,2,1,2 145 | 1628386,21901233,Center,7,9,14,2,3,0,0 146 | 1628964,21901233,Center,0,3,0,1,1,1,0 147 | 1629743,21901233,Center,3,3,8,2,1,0,1 148 | 1628418,21901235,Center,4,12,10,1,6,2,0 149 | 1629028,21901235,Center,11,14,24,3,9,0,2 150 | 2730,21901243,Center,2,2,5,2,3,0,1 151 | 201188,21901243,Center,3,7,8,0,7,0,0 152 | 201580,21901243,Center,1,3,3,1,3,0,1 153 | 202355,21901245,Center,2,2,5,1,4,0,0 154 | 202683,21901245,Center,4,7,11,5,3,0,1 155 | 203994,21901245,Center,12,20,30,3,6,2,1 156 | 202696,21901247,Center,9,14,23,2,10,2,0 157 | 203920,21901247,Center,0,1,5,3,2,0,0 158 | 1628964,21901247,Center,0,1,0,1,0,0,1 159 | 1626246,21901249,Center,4,6,8,2,1,0,0 160 | 1629028,21901249,Center,3,8,7,3,5,0,0 161 | 201188,21901250,Center,1,2,6,1,5,1,0 162 | 1628389,21901250,Center,5,9,10,1,7,1,1 163 | 1626167,21901252,Center,7,13,17,2,7,0,2 164 | 1628418,21901252,Center,9,12,20,4,7,0,3 165 | 202685,21901253,Center,5,9,13,1,12,1,4 166 | 1629637,21901253,Center,0,0,2,0,2,0,0 167 | 201143,21901254,Center,4,7,9,3,3,0,2 168 | 203954,21901254,Center,9,17,27,2,7,1,0 169 | 1627751,21901254,Center,2,4,4,3,1,1,1 170 | 2730,21901255,Center,4,4,11,1,4,1,1 171 | 201580,21901255,Center,0,1,0,0,1,0,0 172 | 203497,21901255,Center,6,6,16,2,11,0,1 173 | 1628396,21901255,Center,2,2,5,3,2,0,1 174 | 201577,21901256,Center,1,4,2,1,1,0,2 175 | 1629743,21901256,Center,2,4,4,3,6,1,2 176 | 1627826,21901258,Center,7,9,18,4,8,0,1 177 | 1629028,21901258,Center,9,18,19,3,4,0,0 178 | 202683,21901260,Center,5,6,10,5,0,0,0 179 | 1628389,21901260,Center,5,12,21,3,9,1,1 180 | 202355,21901261,Center,4,5,9,1,6,0,1 181 | 203994,21901261,Center,6,15,18,6,13,1,3 182 | 202685,21901262,Center,8,17,21,4,10,0,0 183 | 203476,21901262,Center,1,4,2,0,0,0,1 184 | 203497,21901262,Center,5,9,21,5,11,2,3 185 | 1628396,21901262,Center,2,2,4,2,1,0,0 186 | 201143,21901263,Center,1,3,2,0,10,0,0 187 | 203954,21901263,Center,11,20,29,1,10,3,2 188 | 1628394,21901263,Center,0,0,0,0,2,0,0 189 | 1628418,21901263,Center,8,17,19,4,6,2,4 190 | 203999,21901264,Center,10,17,25,3,1,1,0 191 | 1627751,21901264,Center,1,5,2,3,4,0,0 192 | 201580,21901265,Center,3,4,6,1,5,0,1 193 | 203457,21901265,Center,0,1,0,0,4,2,1 194 | 203500,21901265,Center,7,10,18,1,6,0,0 195 | 201572,21901269,Center,4,9,17,1,3,2,2 196 | 1628389,21901269,Center,2,10,6,1,4,1,0 197 | 1626167,21901270,Center,7,12,17,3,5,1,3 198 | 1629028,21901270,Center,11,18,23,3,7,2,4 199 | 1629048,21901270,Center,1,4,3,0,1,0,1 200 | 1626246,21901271,Center,1,2,2,0,4,0,1 201 | 1627826,21901271,Center,10,10,21,6,9,0,1 202 | 202355,21901272,Center,1,5,2,2,1,0,2 203 | 203994,21901272,Center,9,20,22,4,3,1,2 204 | 203999,21901272,Center,3,8,8,3,2,0,0 205 | 1629626,21901272,Center,4,7,9,1,4,1,1 206 | 2730,21901273,Center,2,4,8,4,11,1,0 207 | 202334,21901274,Center,0,2,0,0,2,0,0 208 | 1627751,21901274,Center,9,12,19,4,6,0,3 209 | 1628396,21901274,Center,7,9,15,2,9,1,3 210 | 1628418,21901278,Center,9,18,22,4,4,2,2 211 | 1629637,21901278,Center,2,2,6,2,1,0,0 212 | 203994,21901280,Center,3,8,10,2,11,2,0 213 | 1627826,21901280,Center,2,5,9,6,6,0,0 214 | 2730,21901282,Center,3,5,10,3,9,1,1 215 | 201580,21901282,Center,2,3,5,2,2,2,0 216 | 1626167,21901282,Center,2,8,7,1,2,1,1 217 | 1629048,21901282,Center,3,4,7,1,1,1,1 218 | 1628418,21901285,Center,2,8,9,2,3,0,2 219 | 1627751,21901287,Center,2,4,4,5,9,2,0 220 | 1629637,21901287,Center,2,2,6,0,3,1,0 221 | 202683,21901288,Center,1,4,4,4,4,0,0 222 | 202696,21901288,Center,11,27,26,2,9,0,0 223 | 203920,21901288,Center,0,0,4,3,2,0,0 224 | 1629057,21901288,Center,4,5,8,2,1,1,1 225 | 201143,21901289,Center,6,9,15,0,6,0,1 226 | 203124,21901289,Center,0,2,0,0,2,0,0 227 | 203658,21901289,Center,0,1,1,0,2,1,1 228 | 203954,21901289,Center,1,6,2,2,2,0,0 229 | 203994,21901289,Center,5,9,15,1,5,0,3 230 | 1627826,21901291,Center,4,8,8,5,10,0,1 231 | 1628386,21901291,Center,7,9,16,4,12,1,2 232 | 1629028,21901292,Center,5,10,10,3,3,0,0 233 | 202334,21901293,Center,3,4,7,2,0,1,0 234 | 203497,21901293,Center,3,5,9,2,3,0,1 235 | 1626246,21901293,Center,7,11,20,1,8,1,2 236 | 1628396,21901293,Center,2,2,5,2,3,0,1 237 | 1626167,21901295,Center,3,7,9,0,7,0,2 238 | 1628389,21901295,Center,4,10,10,2,7,0,3 239 | 1629048,21901295,Center,1,2,2,0,1,0,0 240 | 202696,21901297,Center,5,15,12,1,9,1,0 241 | 203920,21901297,Center,4,7,12,4,5,2,0 242 | 1629743,21901297,Center,5,7,10,3,6,1,2 243 | 1627751,21901298,Center,6,7,14,3,9,1,1 244 | 203124,21901299,Center,4,6,9,1,9,2,1 245 | 203658,21901299,Center,2,4,4,3,2,0,2 246 | 1629028,21901299,Center,4,10,8,3,9,0,0 247 | 201572,21901303,Center,9,11,24,1,2,0,0 248 | 201577,21901303,Center,3,6,8,1,3,0,0 249 | 1628394,21901303,Center,4,9,10,3,3,1,0 250 | 1628418,21901303,Center,5,9,13,3,5,0,1 251 | 1626167,21901304,Center,5,14,18,7,5,1,1 252 | 1629048,21901304,Center,2,6,4,1,2,2,1 253 | 203457,21901306,Center,1,1,2,3,4,0,0 254 | 203500,21901306,Center,4,7,8,2,6,1,0 255 | 1628389,21901306,Center,3,5,6,0,2,0,0 256 | 202355,21901309,Center,5,6,16,5,4,1,2 257 | 203994,21901309,Center,11,17,22,2,8,1,0 258 | 1628386,21901309,Center,5,8,14,6,5,0,3 259 | 201572,21901311,Center,6,14,19,4,5,0,1 260 | 201577,21901311,Center,2,9,5,0,4,0,1 261 | 202685,21901311,Center,11,17,26,3,16,0,0 262 | 203476,21901311,Center,1,6,7,1,3,0,0 263 | 1626246,21901313,Center,9,14,18,6,14,0,0 264 | 1629028,21901313,Center,5,10,11,3,6,2,1 265 | 202334,21901314,Center,5,6,11,4,2,0,0 266 | 1627751,21901314,Center,5,6,10,2,3,1,2 267 | 1628396,21901314,Center,3,7,6,3,7,0,1 268 | 203457,21901317,Center,2,2,5,0,1,0,1 269 | 203500,21901317,Center,0,2,0,2,2,0,0 270 | 1627826,21901317,Center,2,3,6,1,3,0,1 271 | 201188,21901318,Center,1,3,2,0,3,1,1 272 | 203999,21901318,Center,1,1,2,0,1,0,0 273 | 1629608,21901318,Center,2,6,4,3,4,0,0 274 | 1629626,21901318,Center,2,6,8,1,3,0,3 275 | -------------------------------------------------------------------------------- /data/problems/combine2/forward.csv: -------------------------------------------------------------------------------- 1 | player_id,game_id,pos,fgm,fga,pts,oreb,dreb,stl,blk 2 | 2544,21900002,Forward,7,19,18,1,9,1,1 3 | 201162,21900002,Forward,2,2,6,0,0,0,0 4 | 202335,21900002,Forward,1,3,4,1,2,0,0 5 | 202695,21900002,Forward,10,19,30,1,5,2,1 6 | 203076,21900002,Forward,8,21,25,3,6,1,2 7 | 203090,21900002,Forward,4,7,10,2,2,4,2 8 | 203210,21900002,Forward,4,8,12,1,5,0,0 9 | 1626149,21900002,Forward,7,11,17,2,5,1,1 10 | 202330,21900008,Forward,8,15,25,0,5,0,0 11 | 202699,21900008,Forward,6,11,15,1,14,2,0 12 | 203118,21900008,Forward,2,7,6,1,5,0,0 13 | 203516,21900008,Forward,0,3,3,1,5,0,0 14 | 1628369,21900008,Forward,8,22,21,1,9,2,0 15 | 1628400,21900008,Forward,0,1,0,0,1,0,0 16 | 1628464,21900008,Forward,0,1,3,1,0,0,1 17 | 1629684,21900008,Forward,0,1,0,0,1,0,0 18 | 2544,21900054,Forward,8,15,23,0,2,0,1 19 | 201162,21900054,Forward,0,0,0,0,0,1,0 20 | 203076,21900054,Forward,7,17,40,8,12,0,2 21 | 203109,21900054,Forward,3,8,9,1,4,0,0 22 | 203524,21900054,Forward,2,6,8,2,2,2,1 23 | 203937,21900054,Forward,1,4,2,0,3,0,0 24 | 203998,21900054,Forward,0,3,0,1,1,0,0 25 | 1628991,21900054,Forward,2,10,7,2,4,1,0 26 | 1629634,21900054,Forward,4,9,10,1,0,1,1 27 | 201145,21900063,Forward,3,8,8,0,1,3,1 28 | 202335,21900063,Forward,1,4,3,1,2,0,0 29 | 202711,21900063,Forward,5,11,14,0,4,0,0 30 | 203090,21900063,Forward,1,3,2,0,2,0,0 31 | 203210,21900063,Forward,8,11,23,1,7,0,1 32 | 204060,21900063,Forward,4,8,10,0,2,2,0 33 | 1626149,21900063,Forward,5,9,10,0,2,0,1 34 | 1626220,21900063,Forward,0,0,0,0,4,1,0 35 | 1629662,21900063,Forward,0,1,0,0,0,0,0 36 | 201145,21900110,Forward,1,6,3,0,6,0,0 37 | 202699,21900110,Forward,4,11,16,1,6,0,0 38 | 202711,21900110,Forward,9,18,20,0,6,1,1 39 | 203118,21900110,Forward,2,3,5,0,2,0,0 40 | 203516,21900110,Forward,2,2,4,0,1,0,0 41 | 204060,21900110,Forward,6,11,16,1,1,0,0 42 | 1626220,21900110,Forward,1,2,5,0,4,1,1 43 | 1627777,21900110,Forward,0,1,1,0,2,0,0 44 | 202329,21900133,Forward,1,2,2,0,1,0,0 45 | 203200,21900133,Forward,2,2,5,0,2,1,0 46 | 203926,21900133,Forward,7,13,18,2,4,0,0 47 | 203932,21900133,Forward,5,10,13,1,1,0,1 48 | 203933,21900133,Forward,9,16,19,0,2,1,0 49 | 203960,21900133,Forward,1,1,2,0,1,1,0 50 | 1627734,21900133,Forward,9,18,21,3,13,1,1 51 | 1628371,21900133,Forward,4,9,10,0,3,0,2 52 | 1628388,21900133,Forward,1,5,2,2,2,0,1 53 | 1628993,21900133,Forward,0,1,0,2,1,0,0 54 | 202329,21900185,Forward,2,8,6,1,5,0,0 55 | 202722,21900185,Forward,5,11,15,3,5,2,0 56 | 203932,21900185,Forward,5,16,13,1,8,0,0 57 | 1628411,21900185,Forward,0,0,2,0,2,1,1 58 | 1629021,21900185,Forward,5,8,12,1,2,0,0 59 | 1629060,21900185,Forward,3,7,6,1,3,0,0 60 | 201959,21900189,Forward,2,6,7,6,2,0,0 61 | 202694,21900189,Forward,5,13,23,3,3,1,2 62 | 203944,21900189,Forward,12,17,30,2,5,2,1 63 | 1626171,21900189,Forward,3,8,7,1,6,1,1 64 | 1626224,21900189,Forward,2,5,5,1,7,0,0 65 | 1628035,21900189,Forward,4,10,14,2,4,0,0 66 | 1628995,21900189,Forward,2,10,6,1,2,1,0 67 | 1629628,21900189,Forward,5,13,15,1,1,2,2 68 | 1629731,21900189,Forward,0,1,0,0,0,1,0 69 | 201145,21900210,Forward,3,8,10,1,2,0,0 70 | 202711,21900210,Forward,9,18,30,2,2,1,0 71 | 203496,21900210,Forward,4,5,11,0,3,2,3 72 | 203952,21900210,Forward,10,24,22,2,5,0,0 73 | 204060,21900210,Forward,1,4,3,2,2,2,0 74 | 1626220,21900210,Forward,1,3,3,0,6,2,0 75 | 1627777,21900210,Forward,0,1,1,0,1,0,0 76 | 1628966,21900210,Forward,1,1,3,0,0,0,0 77 | 1629103,21900210,Forward,4,8,10,1,6,0,1 78 | 201933,21900241,Forward,5,9,17,0,5,0,0 79 | 202329,21900241,Forward,1,7,2,0,4,0,0 80 | 202693,21900241,Forward,2,5,5,0,1,1,1 81 | 1626174,21900241,Forward,4,5,12,1,3,1,1 82 | 1627748,21900241,Forward,0,1,3,1,1,0,0 83 | 1628371,21900241,Forward,4,11,10,0,6,1,4 84 | 1628411,21900241,Forward,1,6,3,0,2,0,0 85 | 202329,21900269,Forward,2,7,5,1,1,2,0 86 | 203932,21900269,Forward,2,11,8,1,7,2,1 87 | 1626178,21900269,Forward,2,3,6,3,3,1,0 88 | 1627783,21900269,Forward,4,22,10,5,8,1,1 89 | 1628371,21900269,Forward,4,13,8,5,5,0,2 90 | 1628384,21900269,Forward,1,5,2,1,6,3,0 91 | 1628449,21900269,Forward,2,4,7,0,3,1,2 92 | 200752,21900276,Forward,2,9,6,2,4,1,0 93 | 202331,21900276,Forward,2,11,5,1,7,0,0 94 | 202335,21900276,Forward,1,1,3,0,0,0,0 95 | 202695,21900276,Forward,8,23,19,1,6,2,1 96 | 203090,21900276,Forward,3,5,9,1,1,3,2 97 | 203210,21900276,Forward,5,8,16,1,7,0,0 98 | 1626149,21900276,Forward,6,12,12,2,5,0,1 99 | 1626168,21900276,Forward,4,8,10,1,3,1,0 100 | 1629234,21900276,Forward,0,1,0,0,0,0,0 101 | 203109,21900286,Forward,4,11,12,2,8,0,0 102 | 203496,21900286,Forward,4,9,13,0,3,1,1 103 | 203524,21900286,Forward,4,10,11,2,3,1,0 104 | 203952,21900286,Forward,6,17,18,1,5,1,2 105 | 203998,21900286,Forward,5,14,12,5,8,3,0 106 | 1628966,21900286,Forward,4,7,10,0,5,1,0 107 | 1628991,21900286,Forward,5,10,12,0,2,0,1 108 | 1629634,21900286,Forward,0,3,0,0,1,0,0 109 | 203922,21900295,Forward,3,12,6,1,3,0,1 110 | 203953,21900295,Forward,4,15,8,2,3,2,1 111 | 1626172,21900295,Forward,1,3,2,0,2,0,2 112 | 1627737,21900295,Forward,4,11,8,3,2,1,0 113 | 1628981,21900295,Forward,0,1,0,0,1,0,0 114 | 1629016,21900295,Forward,4,11,10,5,4,2,1 115 | 1629629,21900295,Forward,3,10,8,1,3,1,1 116 | 1629631,21900295,Forward,6,14,18,1,3,1,0 117 | 1629672,21900295,Forward,9,11,24,5,4,0,0 118 | 203932,21900308,Forward,13,15,32,0,5,1,0 119 | 203967,21900308,Forward,3,7,10,3,3,0,0 120 | 1626162,21900308,Forward,3,8,8,1,2,2,0 121 | 1626163,21900308,Forward,10,12,23,1,4,1,0 122 | 1627767,21900308,Forward,6,8,16,1,3,2,0 123 | 1628371,21900308,Forward,3,9,7,2,2,2,1 124 | 1628411,21900308,Forward,2,2,4,0,2,0,1 125 | 1628518,21900308,Forward,0,0,0,0,1,0,0 126 | 1628969,21900308,Forward,5,8,15,0,1,0,0 127 | 1629059,21900308,Forward,1,2,4,0,0,0,0 128 | 1629661,21900308,Forward,4,9,12,0,1,1,0 129 | 2544,21900329,Forward,11,23,31,1,6,0,1 130 | 2546,21900329,Forward,4,13,15,1,2,3,0 131 | 201229,21900329,Forward,0,0,0,0,2,1,0 132 | 203076,21900329,Forward,12,21,39,0,9,2,3 133 | 1626209,21900329,Forward,1,4,4,0,2,0,0 134 | 1627746,21900329,Forward,3,4,6,0,1,0,1 135 | 1628398,21900329,Forward,6,13,15,0,6,0,0 136 | 1629642,21900329,Forward,1,2,2,0,0,0,2 137 | 201567,21900332,Forward,2,7,6,0,7,0,0 138 | 202699,21900332,Forward,6,10,15,1,3,0,0 139 | 203089,21900332,Forward,3,7,6,2,1,0,0 140 | 203118,21900332,Forward,9,12,21,2,4,0,0 141 | 203516,21900332,Forward,3,6,8,0,7,2,0 142 | 1626204,21900332,Forward,1,4,3,0,2,0,0 143 | 1626224,21900332,Forward,1,2,2,0,0,0,0 144 | 1628035,21900332,Forward,2,7,5,2,1,0,0 145 | 202331,21900343,Forward,10,26,36,0,9,2,1 146 | 202335,21900343,Forward,1,5,5,0,0,1,0 147 | 203090,21900343,Forward,3,8,8,5,9,0,1 148 | 203200,21900343,Forward,1,9,3,3,2,1,0 149 | 203926,21900343,Forward,6,9,17,1,5,0,0 150 | 203933,21900343,Forward,2,8,12,0,8,1,0 151 | 1626149,21900343,Forward,12,21,26,2,5,0,3 152 | 1627734,21900343,Forward,7,18,18,4,18,1,1 153 | 202722,21900352,Forward,11,18,32,2,7,0,1 154 | 203077,21900352,Forward,2,5,4,1,2,0,0 155 | 203469,21900352,Forward,5,6,14,2,8,1,0 156 | 1628970,21900352,Forward,6,14,16,0,2,0,2 157 | 1629021,21900352,Forward,3,7,9,3,3,0,1 158 | 1629023,21900352,Forward,5,13,15,0,4,0,1 159 | 1629060,21900352,Forward,7,16,18,4,8,1,0 160 | 200752,21900396,Forward,4,9,12,2,10,1,1 161 | 200757,21900396,Forward,0,2,0,1,0,0,0 162 | 200782,21900396,Forward,3,6,9,2,9,2,0 163 | 1626168,21900396,Forward,3,6,8,0,4,0,0 164 | 1627863,21900396,Forward,2,8,4,2,2,2,0 165 | 1629109,21900396,Forward,0,3,3,1,3,0,1 166 | 200794,21900414,Forward,4,9,15,5,7,0,0 167 | 203486,21900414,Forward,3,6,9,3,3,0,0 168 | 203924,21900414,Forward,3,8,8,0,1,0,0 169 | 203932,21900414,Forward,4,14,9,1,6,1,1 170 | 1628371,21900414,Forward,3,6,7,2,3,0,0 171 | 1628411,21900414,Forward,1,1,4,0,0,0,0 172 | 1628470,21900414,Forward,0,1,0,0,0,0,0 173 | 1629008,21900414,Forward,2,3,4,0,3,1,1 174 | 2546,21900428,Forward,1,6,5,3,3,0,3 175 | 201229,21900428,Forward,1,2,3,1,0,1,0 176 | 203932,21900428,Forward,4,17,11,6,5,0,1 177 | 1626209,21900428,Forward,0,2,0,0,3,0,0 178 | 1627746,21900428,Forward,2,4,8,2,4,0,2 179 | 1628371,21900428,Forward,3,9,10,1,3,4,2 180 | 1628411,21900428,Forward,1,3,2,3,0,0,0 181 | 1628518,21900428,Forward,0,0,0,0,2,0,0 182 | 200794,21900459,Forward,0,2,0,1,1,2,0 183 | 202324,21900459,Forward,3,9,8,3,10,1,3 184 | 203486,21900459,Forward,2,4,4,1,4,0,1 185 | 203924,21900459,Forward,6,10,17,1,1,0,0 186 | 1627742,21900459,Forward,11,18,31,1,6,2,1 187 | 1629008,21900459,Forward,2,3,5,0,1,0,0 188 | 201568,21900503,Forward,8,14,20,0,3,1,0 189 | 203939,21900503,Forward,3,5,11,2,2,2,1 190 | 1627827,21900503,Forward,1,8,6,1,1,0,1 191 | 1627846,21900503,Forward,1,2,2,0,0,0,1 192 | 1628382,21900503,Forward,2,8,8,0,4,1,0 193 | 1628467,21900503,Forward,5,10,14,6,8,0,0 194 | 1629029,21900503,Forward,12,29,35,2,8,1,0 195 | 1629647,21900503,Forward,0,5,2,0,3,0,1 196 | 1628371,21900504,Forward,1,2,2,1,0,1,0 197 | 1628411,21900504,Forward,4,7,10,1,5,1,0 198 | 1628518,21900504,Forward,0,1,2,2,4,0,1 199 | 1629140,21900504,Forward,1,1,2,1,5,0,1 200 | 201586,21900510,Forward,9,21,19,3,7,0,2 201 | 202710,21900510,Forward,2,10,8,3,9,2,0 202 | 203086,21900510,Forward,3,7,8,0,2,0,0 203 | 203482,21900510,Forward,0,0,0,0,1,0,0 204 | 1626169,21900510,Forward,0,1,0,0,0,0,0 205 | 1626178,21900510,Forward,4,8,13,3,4,1,1 206 | 1627884,21900510,Forward,4,13,10,1,5,2,1 207 | 1628384,21900510,Forward,5,12,12,1,11,2,0 208 | 1628449,21900510,Forward,1,1,2,2,6,0,2 209 | 1629130,21900510,Forward,2,4,6,0,4,0,0 210 | 1629735,21900510,Forward,2,2,4,0,1,0,1 211 | 203110,21900532,Forward,0,2,2,1,5,0,1 212 | 203922,21900532,Forward,1,11,4,1,5,1,0 213 | 1626174,21900532,Forward,2,6,9,2,4,1,1 214 | 1627737,21900532,Forward,3,6,8,3,1,2,0 215 | 1627748,21900532,Forward,0,0,2,0,3,0,0 216 | 1629016,21900532,Forward,8,11,23,2,3,4,1 217 | 1629635,21900532,Forward,6,10,16,0,10,2,1 218 | 1629672,21900532,Forward,4,7,9,0,2,1,0 219 | 201959,21900534,Forward,4,6,11,1,4,1,0 220 | 202331,21900534,Forward,9,14,32,0,4,2,0 221 | 202335,21900534,Forward,2,5,6,0,2,0,0 222 | 202694,21900534,Forward,13,19,38,2,3,2,1 223 | 203090,21900534,Forward,1,4,2,2,4,0,2 224 | 203210,21900534,Forward,4,7,11,4,6,0,0 225 | 203944,21900534,Forward,6,14,16,0,8,2,0 226 | 1626149,21900534,Forward,13,21,34,2,4,0,0 227 | 1626171,21900534,Forward,2,6,8,1,1,0,0 228 | 1628995,21900534,Forward,1,2,2,0,0,0,1 229 | 1629628,21900534,Forward,7,11,24,1,5,1,1 230 | 101141,21900546,Forward,1,2,3,0,0,0,0 231 | 200752,21900546,Forward,6,10,17,1,7,0,0 232 | 201960,21900546,Forward,0,1,0,0,0,0,0 233 | 203114,21900546,Forward,5,16,15,0,5,0,0 234 | 203507,21900546,Forward,10,22,24,2,10,3,1 235 | 1626168,21900546,Forward,2,11,6,3,9,1,0 236 | 1628391,21900546,Forward,0,1,0,0,0,0,0 237 | 1629002,21900546,Forward,1,1,2,0,1,0,0 238 | 201567,21900548,Forward,12,15,30,0,9,1,0 239 | 1626174,21900548,Forward,2,7,6,0,6,0,0 240 | 1626224,21900548,Forward,7,16,17,1,0,1,0 241 | 1627748,21900548,Forward,2,4,5,0,2,0,0 242 | 1629635,21900548,Forward,6,9,15,1,1,3,0 243 | 1629731,21900548,Forward,1,1,3,0,2,0,0 244 | 201959,21900561,Forward,2,5,4,0,0,0,0 245 | 202711,21900561,Forward,7,14,20,0,3,0,1 246 | 204060,21900561,Forward,4,10,11,1,1,0,0 247 | 1626171,21900561,Forward,5,17,13,7,6,0,0 248 | 1626220,21900561,Forward,3,5,8,0,7,1,0 249 | 1627777,21900561,Forward,4,9,11,0,4,1,1 250 | 1628995,21900561,Forward,2,10,6,1,5,0,0 251 | 1629628,21900561,Forward,3,10,10,0,2,1,0 252 | 1629649,21900561,Forward,1,2,3,0,0,0,0 253 | 202330,21900578,Forward,8,11,19,1,4,1,1 254 | 1627742,21900578,Forward,4,15,16,1,2,1,1 255 | 1628369,21900578,Forward,16,22,41,0,6,3,0 256 | 1628400,21900578,Forward,2,3,5,1,2,0,0 257 | 1628464,21900578,Forward,3,7,6,1,3,2,1 258 | 1629684,21900578,Forward,0,0,0,0,1,0,0 259 | 1629740,21900578,Forward,2,4,7,1,4,2,0 260 | 201949,21900584,Forward,6,7,19,2,2,0,1 261 | 201959,21900584,Forward,5,6,14,4,4,1,1 262 | 202710,21900584,Forward,8,15,25,2,9,0,1 263 | 203086,21900584,Forward,2,3,7,1,3,0,0 264 | 203944,21900584,Forward,10,22,26,3,5,2,0 265 | 1626171,21900584,Forward,1,2,4,0,1,0,0 266 | 1627884,21900584,Forward,1,2,3,0,2,0,2 267 | 1628995,21900584,Forward,6,8,17,1,4,0,0 268 | 1629130,21900584,Forward,3,10,11,0,4,0,0 269 | 1629628,21900584,Forward,7,10,23,0,5,2,0 270 | 2772,21900612,Forward,1,3,3,0,1,1,0 271 | 202357,21900612,Forward,5,12,11,4,8,2,0 272 | 203084,21900612,Forward,10,18,25,2,6,1,0 273 | 203939,21900612,Forward,8,10,17,6,3,0,0 274 | 1627827,21900612,Forward,4,8,15,2,6,1,0 275 | 1628382,21900612,Forward,3,9,7,0,1,0,0 276 | 1628385,21900612,Forward,2,3,4,0,2,0,0 277 | 1628467,21900612,Forward,5,9,14,2,2,1,1 278 | 1628963,21900612,Forward,5,9,12,1,6,0,0 279 | 1629029,21900612,Forward,8,18,25,0,15,0,0 280 | 201949,21900644,Forward,9,11,22,0,6,0,2 281 | 202357,21900644,Forward,8,13,22,3,3,1,0 282 | 203084,21900644,Forward,3,11,11,2,3,1,0 283 | 203086,21900644,Forward,3,6,7,1,5,3,0 284 | 203482,21900644,Forward,2,4,5,0,6,1,0 285 | 1627884,21900644,Forward,2,3,7,0,3,0,0 286 | 1628963,21900644,Forward,7,13,15,5,10,1,0 287 | 1629130,21900644,Forward,4,11,12,0,1,0,0 288 | 200757,21900685,Forward,3,5,6,0,3,0,1 289 | 200782,21900685,Forward,1,3,5,3,5,2,2 290 | 203924,21900685,Forward,8,12,25,2,2,0,1 291 | 1627863,21900685,Forward,3,9,11,1,4,1,1 292 | 1628470,21900685,Forward,3,10,7,2,0,1,1 293 | 1629008,21900685,Forward,6,12,17,4,5,0,0 294 | 201163,21900688,Forward,3,4,7,0,3,0,0 295 | 201959,21900688,Forward,5,8,11,3,4,0,2 296 | 202694,21900688,Forward,9,18,21,1,3,1,1 297 | 203944,21900688,Forward,10,22,22,6,9,0,1 298 | 1626171,21900688,Forward,3,6,6,1,3,2,0 299 | 1627752,21900688,Forward,4,10,14,0,3,0,0 300 | 1628995,21900688,Forward,1,2,4,0,0,0,0 301 | 1629066,21900688,Forward,5,9,12,1,2,2,0 302 | 1629651,21900688,Forward,1,1,4,2,3,0,0 303 | 202699,21900702,Forward,6,10,14,0,7,0,0 304 | 203110,21900702,Forward,3,8,9,0,9,1,3 305 | 203118,21900702,Forward,0,2,0,0,2,0,0 306 | 203516,21900702,Forward,1,2,2,0,2,0,0 307 | 203922,21900702,Forward,7,11,20,1,4,1,0 308 | 1627737,21900702,Forward,6,10,15,1,4,0,0 309 | 1629016,21900702,Forward,2,7,5,0,4,0,0 310 | 1629672,21900702,Forward,2,6,4,1,2,0,0 311 | 101141,21900739,Forward,3,9,8,2,2,0,0 312 | 203114,21900739,Forward,9,15,25,1,7,0,1 313 | 203507,21900739,Forward,10,21,30,6,13,1,1 314 | 1626162,21900739,Forward,5,20,15,4,6,1,0 315 | 1627767,21900739,Forward,5,9,11,1,6,0,0 316 | 1628969,21900739,Forward,2,8,7,0,3,2,2 317 | 1629059,21900739,Forward,1,4,2,0,1,1,0 318 | 1629745,21900739,Forward,1,4,2,2,0,0,0 319 | 201586,21900758,Forward,13,21,30,1,6,1,0 320 | 203200,21900758,Forward,7,12,22,2,3,3,1 321 | 203926,21900758,Forward,6,10,19,0,4,0,0 322 | 1626178,21900758,Forward,0,1,0,0,2,1,0 323 | 1627734,21900758,Forward,4,7,15,0,11,0,0 324 | 1627783,21900758,Forward,7,18,25,1,8,3,0 325 | 1628384,21900758,Forward,0,1,0,1,2,1,0 326 | 1628449,21900758,Forward,2,3,7,1,1,0,0 327 | 201152,21900765,Forward,5,10,11,1,5,3,0 328 | 202324,21900765,Forward,4,5,8,3,12,0,1 329 | 1626245,21900765,Forward,1,4,5,5,1,1,0 330 | 1627742,21900765,Forward,5,10,15,0,3,2,0 331 | 1628436,21900765,Forward,1,1,3,0,1,0,0 332 | 1628990,21900765,Forward,6,14,16,2,6,2,0 333 | 1629627,21900765,Forward,9,11,21,1,3,2,1 334 | 1629740,21900765,Forward,5,8,12,1,2,2,0 335 | 202693,21900808,Forward,5,7,14,1,7,0,1 336 | 203089,21900808,Forward,0,0,0,0,1,0,1 337 | 203516,21900808,Forward,2,4,7,0,1,0,0 338 | 203932,21900808,Forward,8,15,25,4,5,1,1 339 | 1626174,21900808,Forward,9,14,26,4,8,0,2 340 | 1627748,21900808,Forward,6,9,18,2,0,1,1 341 | 1628411,21900808,Forward,5,6,11,0,1,0,0 342 | 1629109,21900808,Forward,0,4,0,0,2,0,1 343 | 1629635,21900808,Forward,1,6,3,0,3,2,0 344 | 202711,21900855,Forward,4,10,16,0,3,2,0 345 | 203967,21900855,Forward,5,5,14,0,3,0,0 346 | 204060,21900855,Forward,2,7,9,0,0,0,0 347 | 1626162,21900855,Forward,7,13,18,0,3,1,1 348 | 1626220,21900855,Forward,1,2,5,1,8,2,0 349 | 1627767,21900855,Forward,1,2,2,1,0,0,0 350 | 1627777,21900855,Forward,0,3,0,0,2,0,0 351 | 1628969,21900855,Forward,3,3,8,1,2,0,0 352 | 1629059,21900855,Forward,0,2,0,0,3,0,0 353 | 1629661,21900855,Forward,3,3,8,0,0,1,0 354 | 1629752,21900855,Forward,0,1,0,0,0,0,0 355 | 2544,21900861,Forward,17,27,40,2,6,0,1 356 | 202324,21900861,Forward,4,5,8,0,3,1,1 357 | 202693,21900861,Forward,1,3,4,0,1,0,1 358 | 203076,21900861,Forward,6,21,21,6,8,1,6 359 | 1627742,21900861,Forward,12,20,34,1,6,2,0 360 | 1628398,21900861,Forward,4,10,9,0,1,1,0 361 | 1629627,21900861,Forward,8,18,29,2,4,1,0 362 | 1629740,21900861,Forward,5,8,11,0,6,0,1 363 | 203516,21900867,Forward,5,11,12,0,4,0,0 364 | 203932,21900867,Forward,9,14,25,3,7,1,3 365 | 1628381,21900867,Forward,11,19,26,3,4,0,2 366 | 1628981,21900867,Forward,5,7,10,0,9,1,0 367 | 1629629,21900867,Forward,5,10,14,1,2,2,1 368 | 1629631,21900867,Forward,0,8,2,3,8,0,0 369 | 201567,21900890,Forward,6,9,20,2,10,1,0 370 | 203200,21900890,Forward,1,3,2,0,3,3,0 371 | 203926,21900890,Forward,3,6,9,0,1,0,0 372 | 203933,21900890,Forward,14,20,30,1,2,1,0 373 | 203960,21900890,Forward,0,0,0,0,1,0,0 374 | 1626204,21900890,Forward,1,4,5,0,1,3,0 375 | 1626224,21900890,Forward,3,6,8,0,1,0,0 376 | 1627734,21900890,Forward,8,13,18,3,10,2,1 377 | 202722,21900901,Forward,9,14,29,1,3,0,1 378 | 203952,21900901,Forward,9,19,27,1,5,2,2 379 | 1627733,21900901,Forward,4,12,8,6,4,0,0 380 | 1627737,21900901,Forward,6,8,12,2,11,0,2 381 | 1629021,21900901,Forward,0,0,0,0,1,0,0 382 | 1629060,21900901,Forward,4,8,15,1,7,0,0 383 | 1629308,21900901,Forward,1,6,2,2,3,2,1 384 | 1629672,21900901,Forward,7,14,17,5,2,0,0 385 | 200794,21900913,Forward,6,9,18,7,2,0,0 386 | 203486,21900913,Forward,7,8,16,5,4,0,0 387 | 203924,21900913,Forward,1,4,3,1,3,0,1 388 | 203952,21900913,Forward,9,16,22,1,4,1,1 389 | 1627733,21900913,Forward,5,9,14,1,4,0,0 390 | 1627737,21900913,Forward,7,12,16,1,4,2,1 391 | 1629008,21900913,Forward,1,6,3,1,1,0,0 392 | 1629308,21900913,Forward,3,4,8,0,5,0,1 393 | 1629672,21900913,Forward,8,13,22,1,4,1,0 394 | 201959,21900921,Forward,1,2,2,0,1,0,0 395 | 202711,21900921,Forward,7,15,23,0,4,0,1 396 | 203090,21900921,Forward,1,4,2,0,2,1,0 397 | 203944,21900921,Forward,12,21,32,4,7,0,0 398 | 204060,21900921,Forward,3,4,8,0,5,1,0 399 | 1626171,21900921,Forward,8,15,21,1,3,1,0 400 | 1626220,21900921,Forward,1,8,3,0,4,0,0 401 | 1627777,21900921,Forward,1,2,3,0,1,0,0 402 | 1628995,21900921,Forward,1,5,2,1,4,0,1 403 | 1629628,21900921,Forward,5,12,14,1,3,1,0 404 | 201959,21900969,Forward,4,6,9,0,3,2,3 405 | 203090,21900969,Forward,6,13,14,1,5,1,0 406 | 203944,21900969,Forward,13,22,33,0,11,1,1 407 | 1626171,21900969,Forward,3,7,7,0,2,0,1 408 | 1628381,21900969,Forward,9,20,22,4,11,2,1 409 | 1628981,21900969,Forward,1,3,2,1,1,0,0 410 | 1628995,21900969,Forward,3,7,12,0,3,2,0 411 | 1629628,21900969,Forward,9,14,26,0,5,2,1 412 | 1629629,21900969,Forward,5,13,11,0,6,2,1 413 | 1629631,21900969,Forward,5,12,15,2,4,0,0 414 | 202324,21901231,Forward,1,3,2,2,6,3,0 415 | 204060,21901231,Forward,4,7,13,1,2,2,0 416 | 1626220,21901231,Forward,5,11,12,0,9,2,0 417 | 1627742,21901231,Forward,7,20,23,1,7,0,1 418 | 1627777,21901231,Forward,0,6,0,2,0,0,0 419 | 1629627,21901231,Forward,6,8,13,0,0,0,0 420 | 1629740,21901231,Forward,0,1,0,0,0,3,0 421 | 2544,21901232,Forward,6,19,16,2,9,1,1 422 | 201149,21901232,Forward,0,0,1,2,2,0,1 423 | 202331,21901232,Forward,11,17,30,0,5,3,0 424 | 202335,21901232,Forward,2,2,5,0,4,0,0 425 | 202694,21901232,Forward,0,4,0,0,3,0,0 426 | 202695,21901232,Forward,7,16,28,0,3,2,2 427 | 203076,21901232,Forward,8,19,34,2,6,0,0 428 | 203210,21901232,Forward,3,6,8,0,4,0,1 429 | 1628398,21901232,Forward,4,8,16,0,7,0,1 430 | 202498,21901233,Forward,2,4,6,0,2,0,0 431 | 203516,21901233,Forward,2,2,8,0,3,1,0 432 | 203932,21901233,Forward,2,5,10,0,11,0,0 433 | 1626147,21901233,Forward,0,2,0,1,2,0,1 434 | 1628371,21901233,Forward,6,7,16,1,5,1,0 435 | 1629066,21901233,Forward,2,4,7,1,5,0,0 436 | 1629109,21901233,Forward,1,2,3,0,0,0,0 437 | 203967,21901235,Forward,4,6,16,0,6,1,0 438 | 1626163,21901235,Forward,1,4,2,0,2,0,0 439 | 1628969,21901235,Forward,4,8,13,0,3,1,0 440 | 1629021,21901235,Forward,1,3,2,1,2,0,0 441 | 1629060,21901235,Forward,8,15,21,3,5,1,0 442 | 1629140,21901235,Forward,0,0,1,1,1,0,1 443 | 1629661,21901235,Forward,5,8,12,1,2,1,0 444 | 2544,21901243,Forward,7,15,20,1,9,0,1 445 | 201586,21901243,Forward,2,6,4,1,3,0,2 446 | 202693,21901243,Forward,3,10,6,1,1,2,1 447 | 203076,21901243,Forward,2,7,14,1,5,1,3 448 | 1626178,21901243,Forward,0,1,0,2,1,0,0 449 | 1627783,21901243,Forward,5,17,15,2,7,2,2 450 | 1628384,21901243,Forward,8,9,23,1,3,2,0 451 | 1628398,21901243,Forward,5,13,16,0,4,0,1 452 | 1628449,21901243,Forward,0,1,0,0,0,0,0 453 | 2546,21901245,Forward,5,14,13,1,2,0,1 454 | 202330,21901245,Forward,6,10,22,0,8,1,0 455 | 1626209,21901245,Forward,1,2,2,2,0,2,0 456 | 1628369,21901245,Forward,11,22,34,0,4,2,1 457 | 1628380,21901245,Forward,3,3,6,1,4,1,0 458 | 1628400,21901245,Forward,1,3,3,0,1,0,0 459 | 1628464,21901245,Forward,4,4,9,0,2,1,1 460 | 1629684,21901245,Forward,1,3,2,0,1,0,0 461 | 202357,21901247,Forward,1,4,2,0,3,0,0 462 | 203084,21901247,Forward,4,8,12,1,5,0,0 463 | 203516,21901247,Forward,3,10,8,2,3,1,0 464 | 203932,21901247,Forward,8,12,22,0,5,0,0 465 | 203953,21901247,Forward,1,5,4,0,1,1,0 466 | 1626158,21901247,Forward,5,7,12,1,4,0,0 467 | 1628371,21901247,Forward,1,3,4,0,3,2,0 468 | 1628385,21901247,Forward,9,11,23,4,4,0,0 469 | 1628411,21901247,Forward,1,3,2,1,1,0,0 470 | 1629109,21901247,Forward,1,2,3,2,2,0,1 471 | 203967,21901249,Forward,5,8,13,0,8,0,0 472 | 204001,21901249,Forward,10,20,30,3,5,1,4 473 | 1626163,21901249,Forward,3,6,8,1,1,0,0 474 | 1627827,21901249,Forward,2,8,5,2,8,1,0 475 | 1628467,21901249,Forward,1,4,2,3,2,1,0 476 | 1628969,21901249,Forward,2,9,4,0,1,2,3 477 | 1629029,21901249,Forward,11,20,40,0,8,0,0 478 | 1629661,21901249,Forward,6,11,19,0,12,0,0 479 | 201586,21901250,Forward,7,12,15,1,5,0,0 480 | 202710,21901250,Forward,4,9,16,2,5,2,2 481 | 203109,21901250,Forward,6,14,16,1,5,1,0 482 | 203482,21901250,Forward,6,13,17,3,1,1,1 483 | 1626178,21901250,Forward,0,0,0,0,1,0,0 484 | 1627783,21901250,Forward,7,14,22,0,6,1,0 485 | 1628384,21901250,Forward,1,5,7,1,2,1,1 486 | 1629130,21901250,Forward,1,5,3,0,2,0,1 487 | 203200,21901252,Forward,1,3,2,0,8,0,1 488 | 203926,21901252,Forward,2,6,4,1,2,0,0 489 | 203933,21901252,Forward,14,26,34,4,7,3,4 490 | 203960,21901252,Forward,3,5,8,0,3,1,2 491 | 1629021,21901252,Forward,2,8,5,2,3,2,2 492 | 1629060,21901252,Forward,4,12,9,2,5,0,0 493 | 201229,21901253,Forward,1,2,3,1,1,0,0 494 | 202324,21901253,Forward,1,4,2,2,11,0,1 495 | 203937,21901253,Forward,2,9,5,1,4,0,2 496 | 1627742,21901253,Forward,8,16,24,0,7,2,0 497 | 1628991,21901253,Forward,7,17,22,1,1,1,2 498 | 1629627,21901253,Forward,9,21,23,3,4,0,0 499 | 1629634,21901253,Forward,5,8,10,3,5,0,1 500 | 1629740,21901253,Forward,1,4,5,0,2,0,0 501 | 200752,21901254,Forward,9,15,24,0,4,0,0 502 | 202699,21901254,Forward,10,17,25,1,5,1,1 503 | 203092,21901254,Forward,1,4,2,3,1,0,0 504 | 1629234,21901254,Forward,3,5,6,3,7,0,2 505 | 1629640,21901254,Forward,4,6,15,0,3,0,0 506 | 2544,21901255,Forward,9,16,22,1,7,2,1 507 | 201162,21901255,Forward,0,1,0,0,1,0,0 508 | 203076,21901255,Forward,13,28,42,1,11,3,1 509 | 204060,21901255,Forward,2,5,5,0,2,1,1 510 | 1626220,21901255,Forward,2,7,5,2,11,4,0 511 | 1627777,21901255,Forward,2,5,4,0,1,3,0 512 | 1628398,21901255,Forward,3,6,9,0,1,0,0 513 | 1629752,21901255,Forward,1,1,3,0,0,0,0 514 | 101107,21901256,Forward,2,3,6,3,3,0,2 515 | 101141,21901256,Forward,4,6,11,0,10,0,1 516 | 202498,21901256,Forward,2,7,6,0,2,0,0 517 | 203114,21901256,Forward,3,8,8,0,4,0,0 518 | 203507,21901256,Forward,7,8,16,0,6,0,0 519 | 1626147,21901256,Forward,4,7,11,0,4,0,1 520 | 1628391,21901256,Forward,3,11,8,1,5,0,0 521 | 1629066,21901256,Forward,4,11,10,1,3,2,0 522 | 202331,21901258,Forward,6,17,23,0,6,0,0 523 | 202335,21901258,Forward,1,5,3,0,2,0,0 524 | 202694,21901258,Forward,6,8,16,1,3,0,2 525 | 202695,21901258,Forward,10,21,27,2,5,1,0 526 | 203210,21901258,Forward,4,6,10,0,4,0,0 527 | 203967,21901258,Forward,4,9,13,1,6,0,0 528 | 1626163,21901258,Forward,1,3,3,2,4,0,0 529 | 1628969,21901258,Forward,3,6,9,1,6,2,0 530 | 1629661,21901258,Forward,3,10,8,3,4,1,0 531 | 202330,21901260,Forward,5,13,15,2,5,0,0 532 | 203109,21901260,Forward,3,5,8,1,3,0,0 533 | 203482,21901260,Forward,4,11,15,1,5,2,1 534 | 1627884,21901260,Forward,2,3,6,2,0,0,0 535 | 1628369,21901260,Forward,6,11,23,1,6,1,1 536 | 1628400,21901260,Forward,1,6,3,0,5,1,0 537 | 1628464,21901260,Forward,4,5,11,1,4,0,1 538 | 1629130,21901260,Forward,6,12,21,1,2,0,1 539 | 1629684,21901260,Forward,0,0,0,0,0,0,1 540 | 2546,21901261,Forward,5,14,15,3,8,2,1 541 | 200782,21901261,Forward,2,7,6,1,7,1,0 542 | 201145,21901261,Forward,7,15,22,0,6,0,0 543 | 203496,21901261,Forward,2,11,6,0,8,2,0 544 | 1627863,21901261,Forward,6,14,17,1,3,0,1 545 | 1628380,21901261,Forward,4,9,8,3,4,1,0 546 | 201229,21901262,Forward,2,2,6,1,4,1,0 547 | 203937,21901262,Forward,5,9,12,1,2,1,1 548 | 204060,21901262,Forward,7,13,25,0,4,0,0 549 | 1626220,21901262,Forward,4,8,15,0,7,1,1 550 | 1627777,21901262,Forward,1,5,3,0,0,0,0 551 | 1629634,21901262,Forward,3,7,6,1,4,2,1 552 | 202699,21901263,Forward,8,18,18,1,5,1,1 553 | 1629021,21901263,Forward,2,6,5,0,3,1,0 554 | 1629060,21901263,Forward,2,11,8,2,6,1,0 555 | 200752,21901264,Forward,9,18,24,0,4,1,2 556 | 200794,21901264,Forward,2,6,7,2,5,1,1 557 | 203486,21901264,Forward,2,2,4,1,5,0,0 558 | 203924,21901264,Forward,8,14,22,1,1,1,1 559 | 1628470,21901264,Forward,3,8,8,1,3,0,2 560 | 1629008,21901264,Forward,11,19,30,4,11,0,1 561 | 1629234,21901264,Forward,6,8,12,3,2,1,0 562 | 1629640,21901264,Forward,7,10,20,1,5,1,0 563 | 2544,21901265,Forward,7,19,19,1,10,1,0 564 | 201162,21901265,Forward,0,1,0,0,0,0,0 565 | 201568,21901265,Forward,5,13,19,1,6,1,0 566 | 202693,21901265,Forward,2,6,5,2,2,1,1 567 | 203076,21901265,Forward,3,11,9,2,6,0,0 568 | 1627846,21901265,Forward,2,5,7,0,2,0,0 569 | 1628398,21901265,Forward,3,7,10,0,3,0,0 570 | 1629647,21901265,Forward,0,7,2,0,2,0,0 571 | 101107,21901269,Forward,1,3,2,2,5,1,0 572 | 101141,21901269,Forward,0,1,0,0,0,0,0 573 | 203109,21901269,Forward,5,8,15,0,4,1,0 574 | 203114,21901269,Forward,9,14,33,0,6,0,0 575 | 203482,21901269,Forward,2,8,13,0,6,1,0 576 | 203507,21901269,Forward,13,17,33,1,11,0,1 577 | 203524,21901269,Forward,1,4,3,0,1,2,0 578 | 1627884,21901269,Forward,3,4,12,2,2,0,0 579 | 1629130,21901269,Forward,7,11,21,0,1,0,0 580 | 1629734,21901269,Forward,0,1,0,1,0,0,0 581 | 1629735,21901269,Forward,0,0,0,0,1,0,0 582 | 203200,21901270,Forward,1,7,3,0,1,3,0 583 | 203933,21901270,Forward,7,20,16,2,9,2,0 584 | 203960,21901270,Forward,0,0,0,0,1,0,0 585 | 203967,21901270,Forward,4,9,16,4,4,0,0 586 | 1626163,21901270,Forward,0,3,0,0,4,0,0 587 | 1627767,21901270,Forward,0,0,0,1,0,0,0 588 | 1628388,21901270,Forward,1,1,2,0,0,0,0 589 | 1628969,21901270,Forward,5,9,10,1,3,1,0 590 | 1628993,21901270,Forward,0,0,0,0,1,0,0 591 | 1629661,21901270,Forward,5,13,14,3,9,1,0 592 | 201149,21901271,Forward,0,0,0,1,1,0,0 593 | 202331,21901271,Forward,10,21,24,1,6,0,0 594 | 202335,21901271,Forward,2,4,6,0,1,0,0 595 | 202694,21901271,Forward,5,7,16,0,5,2,0 596 | 202695,21901271,Forward,10,23,29,0,6,1,0 597 | 203210,21901271,Forward,4,4,10,1,4,0,1 598 | 204001,21901271,Forward,9,19,30,2,7,0,1 599 | 1627827,21901271,Forward,5,9,12,3,5,0,1 600 | 1628382,21901271,Forward,2,4,5,0,1,1,0 601 | 1628467,21901271,Forward,3,6,10,2,2,1,1 602 | 1629029,21901271,Forward,10,21,29,0,3,0,0 603 | 2546,21901272,Forward,2,8,7,1,4,0,0 604 | 203486,21901272,Forward,5,9,13,1,6,1,0 605 | 203924,21901272,Forward,7,11,18,0,3,0,1 606 | 1628380,21901272,Forward,3,6,7,3,6,1,1 607 | 1628470,21901272,Forward,5,7,13,0,1,1,1 608 | 1628966,21901272,Forward,1,3,3,1,2,0,0 609 | 1629008,21901272,Forward,10,18,27,3,9,2,1 610 | 200782,21901273,Forward,2,6,6,3,3,2,0 611 | 201145,21901273,Forward,6,13,15,0,4,2,0 612 | 201162,21901273,Forward,1,1,2,0,2,0,0 613 | 202693,21901273,Forward,1,3,3,0,1,0,0 614 | 203076,21901273,Forward,5,8,17,5,7,0,1 615 | 203496,21901273,Forward,3,7,8,0,6,3,2 616 | 1627863,21901273,Forward,5,13,14,0,3,0,1 617 | 1628398,21901273,Forward,8,16,21,0,5,2,2 618 | 200752,21901274,Forward,5,9,14,1,6,3,0 619 | 204060,21901274,Forward,4,8,12,0,3,0,0 620 | 1627777,21901274,Forward,3,11,7,0,8,2,0 621 | 1629234,21901274,Forward,4,5,8,1,4,0,0 622 | 1629640,21901274,Forward,1,2,4,0,5,3,1 623 | 1629714,21901274,Forward,3,8,8,1,5,2,1 624 | 1629752,21901274,Forward,1,2,4,2,3,0,0 625 | 202324,21901278,Forward,5,8,12,4,6,2,0 626 | 1627742,21901278,Forward,4,15,17,0,4,1,0 627 | 1629021,21901278,Forward,0,1,2,1,1,0,0 628 | 1629060,21901278,Forward,10,16,23,1,5,0,0 629 | 1629740,21901278,Forward,4,5,10,0,6,0,3 630 | 2546,21901280,Forward,8,15,21,1,6,0,1 631 | 202331,21901280,Forward,8,16,21,0,6,3,0 632 | 202335,21901280,Forward,3,3,8,1,1,0,0 633 | 202694,21901280,Forward,6,13,15,2,6,1,0 634 | 203210,21901280,Forward,3,7,13,0,8,1,1 635 | 1626209,21901280,Forward,1,2,4,1,2,0,0 636 | 1628380,21901280,Forward,3,5,7,2,5,0,1 637 | 1629117,21901280,Forward,0,0,0,2,2,1,0 638 | 2544,21901282,Forward,13,24,31,1,7,0,1 639 | 201162,21901282,Forward,1,4,2,1,1,2,0 640 | 203076,21901282,Forward,3,14,8,3,5,2,2 641 | 203200,21901282,Forward,1,5,3,1,1,1,1 642 | 203926,21901282,Forward,1,5,3,0,0,0,0 643 | 203933,21901282,Forward,15,22,39,0,5,0,0 644 | 203960,21901282,Forward,2,4,4,2,5,0,0 645 | 1628398,21901282,Forward,3,14,11,3,2,3,1 646 | 201568,21901285,Forward,6,8,20,0,2,1,0 647 | 203488,21901285,Forward,5,11,14,1,2,0,0 648 | 1627784,21901285,Forward,2,3,4,0,2,0,0 649 | 1627846,21901285,Forward,3,6,6,0,3,0,0 650 | 1629021,21901285,Forward,5,8,12,0,3,1,0 651 | 1629060,21901285,Forward,4,11,11,4,4,0,0 652 | 1629140,21901285,Forward,1,2,3,0,1,0,0 653 | 1629647,21901285,Forward,8,13,23,1,6,1,0 654 | 200752,21901287,Forward,5,11,19,1,4,3,1 655 | 202324,21901287,Forward,5,8,10,5,7,0,1 656 | 1627742,21901287,Forward,6,14,17,1,5,1,0 657 | 1629234,21901287,Forward,4,6,8,6,5,2,2 658 | 1629627,21901287,Forward,10,20,25,5,2,0,0 659 | 1629640,21901287,Forward,2,3,9,0,1,2,0 660 | 1629740,21901287,Forward,1,3,2,0,1,0,1 661 | 202330,21901288,Forward,12,18,31,2,7,0,1 662 | 203516,21901288,Forward,4,7,10,2,8,0,0 663 | 1628369,21901288,Forward,10,24,29,1,8,1,1 664 | 1628400,21901288,Forward,1,4,3,0,1,1,0 665 | 1628411,21901288,Forward,2,7,9,0,2,0,1 666 | 1628464,21901288,Forward,3,4,6,2,5,0,3 667 | 1629109,21901288,Forward,5,8,15,3,4,0,2 668 | 1629724,21901288,Forward,0,1,1,1,1,1,0 669 | 2546,21901289,Forward,6,13,20,0,7,2,1 670 | 202699,21901289,Forward,7,19,16,2,5,0,0 671 | 203118,21901289,Forward,4,7,9,2,2,0,0 672 | 203922,21901289,Forward,2,5,4,0,3,1,0 673 | 1626209,21901289,Forward,5,10,12,1,6,2,0 674 | 1628380,21901289,Forward,1,3,2,3,3,1,1 675 | 1629117,21901289,Forward,1,2,3,1,1,0,1 676 | 202335,21901291,Forward,3,5,8,1,1,0,0 677 | 202694,21901291,Forward,6,9,15,1,3,1,0 678 | 202695,21901291,Forward,14,25,39,0,2,4,0 679 | 203210,21901291,Forward,3,8,8,1,2,1,0 680 | 1626147,21901291,Forward,1,1,3,0,4,0,2 681 | 1629066,21901291,Forward,3,6,7,2,3,1,0 682 | 203488,21901292,Forward,4,8,11,1,3,1,0 683 | 203967,21901292,Forward,7,12,16,3,6,1,1 684 | 1626163,21901292,Forward,0,1,0,0,1,0,0 685 | 1627767,21901292,Forward,0,1,2,1,1,1,0 686 | 1627846,21901292,Forward,6,9,15,0,1,2,0 687 | 1628969,21901292,Forward,6,14,18,2,4,1,0 688 | 1628987,21901292,Forward,3,6,8,0,4,0,1 689 | 1629647,21901292,Forward,8,16,22,3,7,1,1 690 | 1629661,21901292,Forward,6,11,18,0,4,0,2 691 | 203077,21901293,Forward,0,0,0,0,6,0,0 692 | 204060,21901293,Forward,3,7,7,0,4,1,1 693 | 1626220,21901293,Forward,0,4,0,1,3,1,0 694 | 1627777,21901293,Forward,4,5,13,0,0,0,0 695 | 1628382,21901293,Forward,1,5,2,0,2,0,0 696 | 1628467,21901293,Forward,5,9,11,0,3,0,1 697 | 1629714,21901293,Forward,1,7,3,0,3,0,1 698 | 202710,21901295,Forward,5,13,19,3,8,4,1 699 | 203109,21901295,Forward,5,8,14,2,6,2,1 700 | 203200,21901295,Forward,2,8,7,0,2,1,0 701 | 203482,21901295,Forward,3,11,8,1,6,1,0 702 | 203926,21901295,Forward,4,11,9,1,4,0,0 703 | 203933,21901295,Forward,5,14,12,1,4,0,0 704 | 203960,21901295,Forward,1,2,4,0,3,0,1 705 | 1627884,21901295,Forward,9,11,18,3,2,1,1 706 | 1628993,21901295,Forward,1,3,3,2,2,0,0 707 | 1629130,21901295,Forward,5,11,14,0,5,0,0 708 | 202498,21901297,Forward,4,5,12,1,2,0,0 709 | 203516,21901297,Forward,3,4,12,0,5,1,1 710 | 1626147,21901297,Forward,1,15,2,0,5,0,1 711 | 1628411,21901297,Forward,5,11,18,0,5,0,0 712 | 1629066,21901297,Forward,1,4,2,1,8,1,1 713 | 1629109,21901297,Forward,1,2,2,1,1,0,2 714 | 1629168,21901297,Forward,2,8,8,2,5,1,0 715 | 1629724,21901297,Forward,2,6,4,1,3,1,0 716 | 200752,21901298,Forward,5,11,13,0,5,1,0 717 | 200782,21901298,Forward,1,5,3,1,0,1,0 718 | 201145,21901298,Forward,6,9,17,0,4,0,0 719 | 201601,21901298,Forward,0,0,0,0,0,2,0 720 | 201960,21901298,Forward,2,5,6,1,0,2,1 721 | 203496,21901298,Forward,2,9,6,2,7,4,2 722 | 203998,21901298,Forward,2,3,5,0,1,1,0 723 | 1629002,21901298,Forward,1,1,2,0,1,1,1 724 | 1629234,21901298,Forward,1,4,4,3,4,0,1 725 | 1629640,21901298,Forward,8,12,24,1,10,0,0 726 | 1629677,21901298,Forward,0,2,0,0,2,0,1 727 | 203118,21901299,Forward,7,12,17,2,2,1,0 728 | 203922,21901299,Forward,5,9,15,3,4,0,0 729 | 203967,21901299,Forward,7,9,18,3,6,0,0 730 | 1626163,21901299,Forward,6,8,13,1,1,1,0 731 | 1628969,21901299,Forward,8,10,24,0,2,2,2 732 | 1629661,21901299,Forward,3,10,6,0,1,0,0 733 | 101141,21901303,Forward,1,6,3,1,3,0,0 734 | 203507,21901303,Forward,1,3,12,2,7,1,0 735 | 203648,21901303,Forward,4,8,8,1,3,0,0 736 | 1627784,21901303,Forward,1,1,3,0,0,0,0 737 | 1628391,21901303,Forward,4,6,11,0,5,0,0 738 | 1629021,21901303,Forward,1,3,6,0,3,0,0 739 | 1629060,21901303,Forward,6,18,20,2,3,0,0 740 | 1629140,21901303,Forward,0,0,1,1,1,0,0 741 | 200782,21901304,Forward,1,6,3,1,3,0,1 742 | 201145,21901304,Forward,5,12,14,1,2,0,1 743 | 201960,21901304,Forward,0,2,3,0,3,1,0 744 | 203200,21901304,Forward,6,10,18,1,5,2,1 745 | 203496,21901304,Forward,3,8,11,0,7,0,2 746 | 203926,21901304,Forward,6,7,16,0,2,1,1 747 | 203960,21901304,Forward,3,3,9,0,5,2,0 748 | 1628993,21901304,Forward,1,3,2,0,8,3,1 749 | 201568,21901306,Forward,2,10,14,0,2,0,0 750 | 202710,21901306,Forward,2,3,7,1,4,2,0 751 | 203086,21901306,Forward,0,4,0,1,4,1,0 752 | 203109,21901306,Forward,1,4,3,0,0,0,0 753 | 203482,21901306,Forward,4,6,9,1,3,0,0 754 | 203488,21901306,Forward,2,5,6,1,4,0,0 755 | 203524,21901306,Forward,4,10,10,0,3,2,0 756 | 1627846,21901306,Forward,5,9,13,0,3,1,0 757 | 1627884,21901306,Forward,2,2,4,0,2,1,0 758 | 1629130,21901306,Forward,6,8,19,0,2,0,0 759 | 1629647,21901306,Forward,8,14,21,3,6,1,1 760 | 1629735,21901306,Forward,3,5,8,2,3,0,0 761 | 2546,21901309,Forward,4,12,9,3,4,1,0 762 | 202498,21901309,Forward,0,0,0,0,1,0,0 763 | 1626147,21901309,Forward,1,2,3,0,1,0,0 764 | 1626209,21901309,Forward,1,5,2,0,1,0,0 765 | 1628380,21901309,Forward,1,7,2,3,3,0,0 766 | 1629066,21901309,Forward,3,5,8,1,4,0,1 767 | 101107,21901311,Forward,2,2,5,0,3,0,0 768 | 201229,21901311,Forward,3,5,9,1,3,1,0 769 | 203114,21901311,Forward,5,13,14,0,4,0,0 770 | 203648,21901311,Forward,1,4,2,0,1,1,0 771 | 203937,21901311,Forward,3,6,10,0,3,2,1 772 | 1628391,21901311,Forward,5,10,12,1,7,1,0 773 | 1629634,21901311,Forward,4,9,10,0,7,1,1 774 | 203077,21901313,Forward,2,5,6,1,2,0,1 775 | 203967,21901313,Forward,6,10,16,0,6,0,0 776 | 1627767,21901313,Forward,3,4,6,0,1,0,0 777 | 1627827,21901313,Forward,0,1,0,0,0,0,0 778 | 1628382,21901313,Forward,5,10,13,0,2,0,0 779 | 1628467,21901313,Forward,1,2,3,1,0,0,1 780 | 1628969,21901313,Forward,6,10,14,0,4,0,1 781 | 1629029,21901313,Forward,5,9,18,1,4,1,0 782 | 1629059,21901313,Forward,0,0,4,0,2,0,0 783 | 1629661,21901313,Forward,7,11,15,2,3,2,1 784 | 204060,21901314,Forward,1,4,3,0,1,1,0 785 | 1626220,21901314,Forward,1,4,2,0,1,0,2 786 | 1627777,21901314,Forward,5,10,13,1,4,0,0 787 | 1629002,21901314,Forward,3,6,10,2,4,1,1 788 | 1629234,21901314,Forward,1,6,8,2,5,0,4 789 | 1629640,21901314,Forward,8,12,24,0,2,1,0 790 | 1629677,21901314,Forward,5,10,16,1,5,0,1 791 | 1629714,21901314,Forward,6,13,13,1,3,1,1 792 | 1629752,21901314,Forward,3,4,6,2,1,1,1 793 | 201149,21901317,Forward,4,7,9,1,5,1,0 794 | 201568,21901317,Forward,0,3,3,0,1,0,0 795 | 202335,21901317,Forward,4,16,17,3,11,0,0 796 | 203210,21901317,Forward,5,11,13,0,8,1,0 797 | 203488,21901317,Forward,1,3,3,0,1,0,1 798 | 1628987,21901317,Forward,2,6,5,1,3,0,0 799 | 1629647,21901317,Forward,3,12,9,1,7,0,2 800 | 200794,21901318,Forward,4,8,10,1,2,2,1 801 | 203486,21901318,Forward,4,6,10,1,2,0,4 802 | 203924,21901318,Forward,1,6,2,1,1,0,0 803 | 203943,21901318,Forward,1,2,2,0,0,0,0 804 | 1626169,21901318,Forward,9,16,23,0,4,1,1 805 | 1626178,21901318,Forward,3,9,6,2,4,0,1 806 | 1628449,21901318,Forward,3,8,9,4,5,0,1 807 | 1628470,21901318,Forward,1,6,3,1,3,0,0 808 | 1628966,21901318,Forward,3,11,11,2,4,1,2 809 | 1629076,21901318,Forward,1,2,4,2,2,2,0 810 | -------------------------------------------------------------------------------- /data/teams.csv: -------------------------------------------------------------------------------- 1 | team_id,full,team,nickname,city,state,founded,division,conference 2 | 1610612737,Atlanta Hawks,ATL,Hawks,Atlanta,Atlanta,1949,Southeast,East 3 | 1610612738,Boston Celtics,BOS,Celtics,Boston,Massachusetts,1946,Atlantic,East 4 | 1610612739,Cleveland Cavaliers,CLE,Cavaliers,Cleveland,Ohio,1970,Central,East 5 | 1610612740,New Orleans Pelicans,NOP,Pelicans,New Orleans,Louisiana,2002,Southwest,West 6 | 1610612741,Chicago Bulls,CHI,Bulls,Chicago,Illinois,1966,Central,East 7 | 1610612742,Dallas Mavericks,DAL,Mavericks,Dallas,Texas,1980,Southwest,West 8 | 1610612743,Denver Nuggets,DEN,Nuggets,Denver,Colorado,1976,Northwest,West 9 | 1610612744,Golden State Warriors,GSW,Warriors,Golden State,California,1946,Pacific,West 10 | 1610612745,Houston Rockets,HOU,Rockets,Houston,Texas,1967,Southwest,West 11 | 1610612746,Los Angeles Clippers,LAC,Clippers,Los Angeles,California,1970,Pacific,West 12 | 1610612747,Los Angeles Lakers,LAL,Lakers,Los Angeles,California,1948,Pacific,West 13 | 1610612748,Miami Heat,MIA,Heat,Miami,Florida,1988,Southeast,East 14 | 1610612749,Milwaukee Bucks,MIL,Bucks,Milwaukee,Wisconsin,1968,Central,East 15 | 1610612750,Minnesota Timberwolves,MIN,Timberwolves,Minnesota,Minnesota,1989,Northwest,West 16 | 1610612751,Brooklyn Nets,BKN,Nets,Brooklyn,New York,1976,Atlantic,East 17 | 1610612752,New York Knicks,NYK,Knicks,New York,New York,1946,Atlantic,East 18 | 1610612753,Orlando Magic,ORL,Magic,Orlando,Florida,1989,Southeast,East 19 | 1610612754,Indiana Pacers,IND,Pacers,Indiana,Indiana,1976,Central,East 20 | 1610612755,Philadelphia 76ers,PHI,76ers,Philadelphia,Pennsylvania,1949,Atlantic,East 21 | 1610612756,Phoenix Suns,PHX,Suns,Phoenix,Arizona,1968,Pacific,West 22 | 1610612757,Portland Trail Blazers,POR,Trail Blazers,Portland,Oregon,1970,Northwest,West 23 | 1610612758,Sacramento Kings,SAC,Kings,Sacramento,California,1948,Pacific,West 24 | 1610612759,San Antonio Spurs,SAS,Spurs,San Antonio,Texas,1976,Southwest,West 25 | 1610612760,Oklahoma City Thunder,OKC,Thunder,Oklahoma City,Oklahoma,1967,Northwest,West 26 | 1610612761,Toronto Raptors,TOR,Raptors,Toronto,Ontario,1995,Atlantic,East 27 | 1610612762,Utah Jazz,UTA,Jazz,Utah,Utah,1974,Northwest,West 28 | 1610612763,Memphis Grizzlies,MEM,Grizzlies,Memphis,Tennessee,1995,Southwest,West 29 | 1610612764,Washington Wizards,WAS,Wizards,Washington,District of Columbia,1961,Southeast,East 30 | 1610612765,Detroit Pistons,DET,Pistons,Detroit,Michigan,1948,Central,East 31 | 1610612766,Charlotte Hornets,CHA,Hornets,Charlotte,North Carolina,1988,Southeast,East 32 | -------------------------------------------------------------------------------- /solutions-to-exercises/02_python_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for Python chapter. 3 | 4 | Questions with written (not code) answers are inside triple quotes. 5 | """ 6 | 7 | ############################################################################### 8 | # 2.1 9 | ############################################################################### 10 | """ 11 | a) `_throwaway_data`. Valid. Python programmers often start variables with `_` 12 | if they're throwaway or temporary, short term variables. 13 | b) `n_shots`. Valid. 14 | c) `3_pt_percentage`. Not valid. Can't start with a number. 15 | d) `numOfBoards`. Valid, though convention is to split words with `_`, not camelCase. 16 | e) `flagrant2`. Valid. Numbers OK as long as they're not in the first spot 17 | f) `coach name`. Not valid. No spaces 18 | g) `@home_or_away`. Not valid. Only non alphanumeric character allowed is `_` 19 | h) `'ft_attempts'`. Not valid. A string (wrapped in quotes), not a variable 20 | name. Again, only non alphanumeric character allowed is `_` 21 | """ 22 | 23 | ############################################################################### 24 | # 2.2 25 | ############################################################################### 26 | weekly_points = 100 27 | weekly_points = weekly_points + 28 28 | weekly_points = weekly_points + 5 29 | 30 | weekly_points # 133 31 | 32 | ############################################################################### 33 | # 2.3 34 | ############################################################################### 35 | def commentary(player, play): 36 | return f'{player} with the {play}!' 37 | 38 | commentary('Lebron', 'dunk') 39 | 40 | ############################################################################### 41 | # 2.4 42 | ############################################################################### 43 | """ 44 | It's a string method, so what might `islower()` in the context of a string? 45 | I'd say it probably returns whether or not the string is lowercase. 46 | 47 | A function "is *something*" usually returns a yes or no answer (is it 48 | something or not), which would mean it returns a boolean. 49 | 50 | We can test it like: 51 | """ 52 | 53 | 'lebron james'.islower() # should return True 54 | 'Lebron James'.islower() # should return False 55 | 56 | ############################################################################### 57 | # 2.5 58 | ############################################################################### 59 | def is_fox(player): 60 | return player.replace("'", '').lower() == 'deaaron fox' 61 | 62 | is_fox('lebron james') 63 | is_fox("De'Aaron Fox") 64 | is_fox("DEAARON FOX") 65 | 66 | ############################################################################### 67 | # 2.6 68 | ############################################################################### 69 | def is_good_score(score): 70 | if score >= 100: 71 | return f'{score} is a good score' 72 | else: 73 | return f"{score}'s not that good" 74 | 75 | is_good_score(90) 76 | is_good_score(130) 77 | 78 | ############################################################################### 79 | # 2.7 80 | ############################################################################### 81 | roster = ['kevin durant', 'kyrie irving', 'james harden'] 82 | 83 | roster[0:2] 84 | roster[:2] 85 | roster[:-1] 86 | [x for x in roster if x != 'james harden'] 87 | [x for x in roster if not x.startswith('j')] 88 | [x for x in roster if x in ['kevin durant', 'kyrie irving']] 89 | 90 | ############################################################################### 91 | # 2.8 92 | ############################################################################### 93 | shot_info = {'shooter': 'Steph Curry', 'is_3pt': True, 'went_in': False} 94 | 95 | # a 96 | shot_info['shooter'] = 'Devon Booker' 97 | shot_info 98 | 99 | # b 100 | def toggle3(info): 101 | info['is_3pt'] = not info['is_3pt'] 102 | return info 103 | 104 | shot_info 105 | toggle3(shot_info) 106 | 107 | ############################################################################### 108 | # 2.9 109 | ############################################################################### 110 | """ 111 | a) No. `'is_ft'` hasn't been defined. 112 | b) No, `shooter` is a variable that hasn't been defined, the key is 113 | `'shooter'`. 114 | c) Yes. 115 | """ 116 | 117 | ############################################################################### 118 | # 2.10 119 | ############################################################################### 120 | roster = ['kevin durant', 'kyrie irving', 'james harden'] 121 | 122 | # a 123 | for x in roster: 124 | print(x.split(' ')[-1]) 125 | 126 | # b 127 | {player: len(player) for player in roster} 128 | 129 | ############################################################################### 130 | # 2.11 131 | ############################################################################### 132 | roster_dict = {'PF': 'kevin durant', 133 | 'SG': 'kyrie irving', 134 | 'PG': 'james harden', 135 | 'C': 'deandre jordan'} 136 | 137 | # a 138 | [pos for pos in roster_dict] 139 | 140 | # b 141 | [player for _, player in roster_dict.items() 142 | if player.split(' ')[-1][0] in ['h', 'j']] 143 | 144 | ############################################################################### 145 | # 2.12 146 | ############################################################################### 147 | # a 148 | def mapper(my_list, my_function): 149 | return [my_function(x) for x in my_list] 150 | 151 | # b 152 | list_of_n_3pt_made = [5, 6, 1, 0, 4, 4] 153 | 154 | mapper(list_of_n_3pt_made, lambda x: x*3) 155 | -------------------------------------------------------------------------------- /solutions-to-exercises/03_pandas_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for Pandas chapter. 3 | 4 | Questions with written (not code) answers are inside triple quotes. 5 | """ 6 | ############################################################################### 7 | # PANDAS BASICS 8 | ############################################################################### 9 | 10 | ####### 11 | # 3.0.1 12 | ####### 13 | import pandas as pd 14 | from os import path 15 | 16 | DATA_DIR = './data' 17 | games = pd.read_csv(path.join(DATA_DIR, 'games.csv')) 18 | 19 | ####### 20 | # 3.0.2 21 | ####### 22 | # works because data is sorted by adp already 23 | games50 = games.head(50) 24 | 25 | # this is better if don't want to assume data is sorted 26 | games50 = games.sort_values('date').head(50) 27 | 28 | ####### 29 | # 3.0.3 30 | ####### 31 | games.sort_values('home_pts', ascending=False, inplace=True) 32 | games.head() 33 | 34 | # Note: if this didn't work when you printed it on a new line in the REPL you 35 | # probably forgot the `inplace=True` argument. 36 | 37 | ####### 38 | # 3.0.4 39 | ####### 40 | type(games.sort_values('home_pts')) # it's a DataFrame 41 | 42 | ####### 43 | # 3.0.5 44 | ####### 45 | # a 46 | games_simple = games[['date', 'home', 'away', 'home_pts', 'away_pts']] 47 | 48 | # b 49 | games_simple = games_simple[['home', 'away', 'date', 'home_pts', 'away_pts']] 50 | 51 | # c 52 | games_simple['game_id'] = games['game_id'] 53 | 54 | # d 55 | games.to_csv(path.join(DATA_DIR, 'games_simple.txt'), sep='|') 56 | 57 | ############################################################################### 58 | # COLUMNS 59 | ############################################################################### 60 | 61 | ####### 62 | # 3.1.1 63 | ####### 64 | import pandas as pd 65 | from os import path 66 | 67 | DATA_DIR = './data' 68 | pg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 69 | 70 | ####### 71 | # 3.1.2 72 | ####### 73 | pg['net_takeaways'] = pg['stl'] - pg['tov'] 74 | pg['net_takeaways'].head() 75 | 76 | ####### 77 | # 3.1.3 78 | ####### 79 | pg['player_desc'] = pg['name'] + ' is the ' + pg['team'] + ' ' + pg['pos'] 80 | pg['player_desc'].head() 81 | 82 | ####### 83 | # 3.1.4 84 | ####### 85 | pg['bad_game'] = (pg['fga'] > 20) & (pg['pts'] < 15) 86 | pg['bad_game'].head() 87 | 88 | ####### 89 | # 3.1.5 90 | ####### 91 | pg['len_last_name'] = (pg['name'] 92 | .apply(lambda x: len(x.split('.')[-1]))) 93 | pg['len_last_name'].head() 94 | 95 | ####### 96 | # 3.1.6 97 | ####### 98 | pg['game_id'] = pg['game_id'].astype(str) 99 | 100 | ####### 101 | # 3.1.7 102 | ####### 103 | # a 104 | pg.columns = [x.replace('_', ' ') for x in pg.columns] 105 | pg.head() 106 | 107 | # b 108 | pg.columns = [x.replace(' ', '_') for x in pg.columns] 109 | pg.head() 110 | 111 | ####### 112 | # 3.1.8 113 | ####### 114 | # a 115 | pg['oreb_percentage'] = pg['oreb']/pg['reb'] 116 | pg['oreb_percentage'].head() 117 | 118 | # b 119 | """ 120 | `'oreb_percentage'` is offensive rebounds divided by total rebounds. Since you 121 | can't divide by 0, `oreb_percentage` is missing whenever a player had 0 rebounds. 122 | """ 123 | 124 | # To replace all the missing values with `-99`: 125 | pg['oreb_percentage'].fillna(-99, inplace=True) 126 | pg['oreb_percentage'].head() 127 | 128 | ####### 129 | # 3.1.9 130 | ####### 131 | pg.drop('oreb_percentage', axis=1, inplace=True) 132 | pg.head() 133 | 134 | # If you forget the `axis=1` Pandas will try to drop the *row* with the 135 | # index value `'oreb_percentage'`. Since that doesn't exist, it'll throw an 136 | # error. 137 | 138 | # Without the `inplace=True`, Pandas just returns a new copy of `pg` without the 139 | # `'oreb_percentage'` column. Nothing happens to the original `pg`, though we 140 | # could reassign it if we wanted like this: 141 | 142 | # alternative to inplace=True 143 | # pg = pg.drop('oreb_percentage', axis=1) 144 | 145 | ############################################################################### 146 | # BUILT-IN FUNCTIONS 147 | ############################################################################### 148 | ####### 149 | # 3.2.1 150 | ####### 151 | import pandas as pd 152 | from os import path 153 | 154 | DATA_DIR = './data' 155 | pg = pd.read_csv(path.join(DATA_DIR, 'player_game.csv')) 156 | 157 | ####### 158 | # 3.2.2 159 | ####### 160 | pg['total_shots1'] = pg['fga'] + pg['fta'] 161 | 162 | pg['total_shots2'] = pg[['fga', 'fta']].sum(axis=1) 163 | 164 | (pg['total_shots1'] == pg['total_shots2']).all() 165 | 166 | ####### 167 | # 3.2.3 168 | ####### 169 | 170 | # a 171 | pg[['pts', 'fga', 'reb']].mean() 172 | 173 | # pts 10.659413 174 | # fga 8.403500 175 | # reb 4.242668 176 | 177 | # b 178 | ((pg['pts'] >= 40) & (pg['reb'] >= 10)).sum() # 3 179 | 180 | # c 181 | (((pg['pts'] >= 40) & (pg['reb'] >= 10)).sum() 182 | /(pg['pts'] >= 40).sum()) 183 | 184 | # d 185 | pg['fg3a'].sum() # 6809 186 | 187 | # e 188 | pg['team'].value_counts() # ORL - 152 times 189 | 190 | ############################################################################### 191 | # FILTERING 192 | ############################################################################### 193 | ####### 194 | # 3.3.1 195 | ####### 196 | import pandas as pd 197 | from os import path 198 | 199 | DATA_DIR = './data' 200 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) 201 | 202 | ####### 203 | # 3.3.2 204 | ####### 205 | # a 206 | dftg_chi1 = dftg.loc[dftg['team'] == 'CHI', 207 | ['team', 'date', 'pts', 'fgm', 'fga']] 208 | dftg_chi1.head() 209 | 210 | # b 211 | dftg_chi2 = dftg.query("team == 'CHI'")[['team', 'date', 'pts', 'fgm', 'fga']] 212 | dftg_chi2.head() 213 | 214 | ####### 215 | # 3.3.3 216 | ####### 217 | dftg_no_chi = dftg.loc[dftg['team'] != 'CHI', ['team', 'date', 'pts', 'fgm', 'fga']] 218 | dftg_no_chi.head() 219 | 220 | ####### 221 | # 3.3.4 222 | ####### 223 | 224 | # a 225 | dftg[['fga', 'fgm', 'fg3a', 'fg3m']].duplicated().any() # yes there are 226 | 227 | dftg[['fga', 'fgm', 'fg3a', 'fg3m']].duplicated().sum() # 32 228 | 229 | # b 230 | # flags ALL dups (not just 2nd) because passing keep=False 231 | dups = dftg[['fga', 'fgm', 'fg3a', 'fg3m']].duplicated(keep=False) 232 | 233 | dftg_fg_dup = dftg.loc[dups] 234 | dftg_fg_no_dup = dftg.loc[~dups] 235 | 236 | ####### 237 | # 3.3.5 238 | ####### 239 | import numpy as np 240 | 241 | dftg['three_pt_desc'] = np.nan 242 | dftg.loc[dftg['fg3_pct'] > .5, 'three_pt_desc'] = 'great' 243 | dftg.loc[dftg['fg3_pct'] <= .25, 'three_pt_desc'] = 'brutal' 244 | dftg[['fg3_pct', 'three_pt_desc']].sample(5) 245 | 246 | ####### 247 | # 3.3.6 248 | ####### 249 | # a 250 | dftg_no_desc1 = dftg.loc[dftg['three_pt_desc'].isnull()] 251 | 252 | # b 253 | dftg_no_desc2 = dftg.query("three_pt_desc.isnull()") 254 | 255 | ############################################################################### 256 | # GRANULARITY 257 | ############################################################################### 258 | ####### 259 | # 3.4.1 260 | ####### 261 | """ 262 | Usually you can only shift your data from more (play by play) to less (game) 263 | granular, which necessarily results in a loss of information. If I go from 264 | knowing whether or not Lebron made every single shot to just knowing how many 265 | points he scored total, that's a loss of information. 266 | """ 267 | 268 | ####### 269 | # 3.4.2 270 | ####### 271 | 272 | # a 273 | import pandas as pd 274 | from os import path 275 | 276 | DATA_DIR = './data' 277 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) 278 | 279 | # b 280 | dftg.groupby('team')['pts'].mean() 281 | 282 | # c 283 | dftg['gt_100'] = dftg['pts'] >= 100 284 | 285 | dftg.groupby('team')['gt_100'].mean() 286 | 287 | # d 288 | dftg['gt_150'] = dftg['pts'] >= 150 289 | dftg.groupby('team')['gt_150'].any() 290 | 291 | dftg.groupby('team')['gt_150'].any().sum() 292 | 293 | # e 294 | 295 | dftg.groupby('date')['team_id'].count().head() 296 | dftg.groupby('date')['team_id'].sum().head() 297 | 298 | """ 299 | Count counts the number of non missing (non `np.nan`) values. This is different 300 | than `sum` which adds up the values in all of the columns. The only time 301 | `count` and `sum` would return the same thing is if you had a column filled 302 | with 1s without any missing values. 303 | """ 304 | 305 | ####### 306 | # 3.4.3 307 | ####### 308 | 309 | # a 310 | dftg2 = dftg.groupby(['team_id', 'wl']).agg( 311 | ave_pts = ('pts', 'mean'), 312 | ave_fgm = ('fgm', 'mean'), 313 | ave_fga = ('fga', 'mean'), 314 | ave_fg3m = ('fg3m', 'mean'), 315 | ave_fg3a = ('fg3a', 'mean'), 316 | n = ('team_id', 'count')) 317 | 318 | dftg2.head() 319 | 320 | # b 321 | dftg2.reset_index(inplace=True) 322 | 323 | # c 324 | # loc 325 | (dftg2.loc[dftg2['wl'] == 'L', 'ave_pts'] > 110).sum() # 4 326 | 327 | # or with query 328 | (dftg2.query("wl == 'L'")['ave_pts'] > 110).sum() # 4 329 | 330 | # d 331 | dftg3 = dftg.groupby(['team', 'wl']).agg( 332 | ave_pts = ('pts', 'mean'), 333 | ave_fgm = ('fgm', 'mean'), 334 | ave_fga = ('fga', 'mean'), 335 | ave_fg3m = ('fg3m', 'mean'), 336 | ave_fg3a = ('fg3a', 'mean'), 337 | n = ('team', 'count')) 338 | 339 | # e 340 | """ 341 | Stacking is when you change the granularity in your data, but shift information 342 | from rows to columns (or vis versa) so it doesn't result in any loss on 343 | information. 344 | 345 | An example would be going from the team-win/loss level to the team level. If we 346 | stacked it, we'd go from rows being: 347 | 348 | ave_pts ave_fgm ave_fga ave_fg3m ave_fg3a n 349 | team wl 350 | ATL L 107.382979 39.127660 90.936170 11.382979 36.425532 47 351 | W 122.050000 44.200000 89.650000 13.500000 35.200000 20 352 | BKN L 107.216216 39.027027 91.135135 12.459459 38.918919 37 353 | W 116.600000 41.828571 89.314286 13.714286 37.314286 35 354 | BOS L 105.416667 38.041667 90.208333 10.958333 35.541667 24 355 | 356 | To: 357 | 358 | ave_pts ave_fgm ... ave_fg3m ave_fg3a n 359 | wl L W L ... W L W L W 360 | team ... 361 | ATL 107.0 122.0 39.0 ... 14.0 36.0 35.0 47 20 362 | BKN 107.0 117.0 39.0 ... 14.0 39.0 37.0 37 35 363 | BOS 105.0 118.0 38.0 ... 13.0 36.0 34.0 24 48 364 | CHA 100.0 107.0 37.0 ... 13.0 34.0 34.0 42 23 365 | CHI 103.0 114.0 39.0 ... 14.0 36.0 34.0 43 22 366 | """ 367 | 368 | dftg3.unstack().head() 369 | 370 | ############################################################################### 371 | # COMBINING DATAFRAMES 372 | ############################################################################### 373 | ####### 374 | # 3.5.1 375 | ####### 376 | # a 377 | import pandas as pd 378 | from os import path 379 | 380 | DATA_DIR = './data' 381 | df_pts = pd.read_csv(path.join(DATA_DIR, 'problems/combine1', 'pts.csv')) 382 | df_reb = pd.read_csv(path.join(DATA_DIR, 'problems/combine1', 'reb.csv')) 383 | df_def = pd.read_csv(path.join(DATA_DIR, 'problems/combine1', 'def.csv')) 384 | 385 | # b 386 | df_comb1 = pd.merge(df_pts, df_reb) 387 | df_comb1 = pd.merge(df_comb1, df_def, how='left') 388 | 389 | df_comb1 = df_comb1.fillna(0) 390 | 391 | # c 392 | df_comb2 = pd.concat([df_pts.set_index(['player_id', 'game_id']), 393 | df_reb.set_index(['player_id', 'game_id']), 394 | df_def.set_index(['player_id', 'game_id'])], join='outer', 395 | axis=1) 396 | 397 | df_comb2 = df_comb2.fillna(0) 398 | 399 | # d 400 | """ 401 | Which is better is somewhat subjective, but I generally prefer `concat` when 402 | combining three or more DataFrames because you can do it all in one step. 403 | 404 | Note `merge` gives a little more fine grained control over how you merge (left, 405 | or outer) vs `concat`, which just gives you inner vs outer. 406 | 407 | Note also we have to set the index equal to game and player id before 408 | concating. 409 | """ 410 | 411 | ######## 412 | # 3.5.2a 413 | ######## 414 | import pandas as pd 415 | from os import path 416 | 417 | DATA_DIR = './data' 418 | df_c = pd.read_csv(path.join(DATA_DIR, 'problems/combine2', 'center.csv')) 419 | df_f = pd.read_csv(path.join(DATA_DIR, 'problems/combine2', 'forward.csv')) 420 | df_g = pd.read_csv(path.join(DATA_DIR, 'problems/combine2', 'guard.csv')) 421 | 422 | # b 423 | df = pd.concat([df_c, df_f, df_g], ignore_index=True) 424 | 425 | ####### 426 | # 3.5.3 427 | ####### 428 | # a 429 | import pandas as pd 430 | from os import path 431 | 432 | DATA_DIR = './data' 433 | dft = pd.read_csv(path.join(DATA_DIR, 'teams.csv')) 434 | 435 | # b 436 | for conf in ['East', 'West']: 437 | (dft 438 | .query(f"conference == '{conf}'") 439 | .to_csv(path.join(DATA_DIR, f'dft_{conf}.csv'), index=False)) 440 | 441 | # c 442 | df = pd.concat([pd.read_csv(path.join(DATA_DIR, f'dft_{conf}.csv')) 443 | for conf in ['East', 'West']], ignore_index=True) 444 | 445 | -------------------------------------------------------------------------------- /solutions-to-exercises/04_sql_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for SQL chapter. 3 | 4 | Note: this assumes you've already created/populated the SQL database as 5 | outlined in the book and ./code/04_sql.py. 6 | """ 7 | import pandas as pd 8 | from os import path 9 | import sqlite3 10 | 11 | DATA_DIR = './data' 12 | 13 | conn = sqlite3.connect(path.join(DATA_DIR, 'basketball-data.sqlite')) 14 | 15 | ############################################################################### 16 | # 4.1 17 | ############################################################################### 18 | df = pd.read_sql( 19 | """ 20 | SELECT date, name, fgm, fga, pts AS points 21 | FROM player_game, team 22 | WHERE team.team = player_game.team AND 23 | team.division = 'Central' 24 | """, conn) 25 | 26 | ############################################################################### 27 | # 4.2 28 | ############################################################################### 29 | df = pd.read_sql( 30 | """ 31 | SELECT p.first, p.last, date, fgm, fga, pts AS points 32 | FROM player_game AS pg, team AS t, player AS p 33 | WHERE t.team = pg.team AND 34 | t.division = 'Central' AND p.player_id = pg.player_id 35 | """, conn) 36 | -------------------------------------------------------------------------------- /solutions-to-exercises/05_api_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercise for api problems. 3 | """ 4 | 5 | import requests 6 | from pandas import DataFrame 7 | import pandas as pd 8 | 9 | # given urls 10 | url_adp = 'https://api.myfantasyleague.com/2019/export?TYPE=adp&JSON=1' 11 | url_player = 'https://api.myfantasyleague.com/2019/export?TYPE=players&JSON=1' 12 | 13 | # call w/ requests 14 | resp_adp = requests.get(url_adp) 15 | resp_player = requests.get(url_player) 16 | 17 | # need to inspect the .json() results in the REPL to see what we want/can turn 18 | # into a DataFrame -- usually list of dicts 19 | df_adp = DataFrame(resp_adp.json()['adp']['player']) 20 | df_player = DataFrame(resp_player.json()['players']['player']) 21 | 22 | # now combine 23 | df = pd.merge(df_adp, df_player) 24 | 25 | # get rid of IDP players, take top 200 26 | df = df.query("position in ('WR', 'RB', 'TE', 'QB', 'Def', 'PK')") 27 | df = df.head(200) 28 | -------------------------------------------------------------------------------- /solutions-to-exercises/05_scraping_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for scraping problems. 3 | """ 4 | 5 | from bs4 import BeautifulSoup as Soup 6 | import requests 7 | from pandas import DataFrame 8 | 9 | ffc_base_url = 'https://fantasyfootballcalculator.com' 10 | 11 | ############################################################################### 12 | # 5.1.1 13 | ############################################################################### 14 | def scrape_ffc(scoring, nteams, year): 15 | # build URL based on arguments 16 | ffc_url = (ffc_base_url + '/adp' + _scoring_helper(scoring) + 17 | f'/{nteams}-team/all/{year}') 18 | ffc_response = requests.get(ffc_url) 19 | 20 | # all same as 05_01_scraping.py file 21 | adp_soup = Soup(ffc_response.text) 22 | tables = adp_soup.find_all('table') 23 | adp_table = tables[0] 24 | rows = adp_table.find_all('tr') 25 | 26 | # move parse_row to own helper function 27 | list_of_parsed_rows = [_parse_row(row) for row in rows[1:]] 28 | 29 | # put it in a dataframe 30 | df = DataFrame(list_of_parsed_rows) 31 | 32 | # clean up formatting 33 | df.columns = ['ovr', 'pick', 'name', 'pos', 'team', 'adp', 'std_dev', 34 | 'high', 'low', 'drafted', 'graph'] 35 | 36 | float_cols =['adp', 'std_dev'] 37 | int_cols =['ovr', 'drafted'] 38 | 39 | df[float_cols] = df[float_cols].astype(float) 40 | df[int_cols] = df[int_cols].astype(int) 41 | 42 | df.drop('graph', axis=1, inplace=True) 43 | 44 | return df 45 | 46 | # helper functions - just moving some logic to own section 47 | def _scoring_helper(scoring): 48 | """ 49 | Take a scoring system (either 'ppr', 'half', 'std') and return the correct 50 | FFC URL fragment. 51 | 52 | Note: helper functions are often prefixed with _, but it's not required. 53 | """ 54 | if scoring == 'ppr': 55 | return '/ppr' 56 | elif scoring == 'half': 57 | return '/half-ppr' 58 | elif scoring == 'std': 59 | return '/standard' 60 | 61 | def _parse_row(row): 62 | """ 63 | Take in a tr tag and get the data out of it in the form of a list of 64 | strings. 65 | """ 66 | return [str(x.string) for x in row.find_all('td')] 67 | 68 | ############################################################################### 69 | # 5.1.2 70 | ############################################################################### 71 | def scrape_ffc_plus(scoring, nteams, year): 72 | # build URL based on arguments 73 | ffc_url = ffc_base_url + '/adp' + _scoring_helper(scoring) + f'/{nteams}-team/all/{year}' 74 | ffc_response = requests.get(ffc_url) 75 | 76 | # all same as 05_01_scraping.py file 77 | adp_soup = Soup(ffc_response.text) 78 | tables = adp_soup.find_all('table') 79 | adp_table = tables[0] 80 | rows = adp_table.find_all('tr') 81 | 82 | # move parse_row to own helper function 83 | list_of_parsed_rows = [_parse_row_with_link(row) for row in rows[1:]] 84 | 85 | # put it in a dataframe 86 | df = DataFrame(list_of_parsed_rows) 87 | 88 | # clean up formatting 89 | df.columns = ['ovr', 'pick', 'name', 'pos', 'team', 'adp', 'std_dev', 90 | 'high', 'low', 'drafted', 'graph', 'link'] 91 | 92 | float_cols =['adp', 'std_dev'] 93 | int_cols =['ovr', 'drafted'] 94 | 95 | df[float_cols] = df[float_cols].astype(float) 96 | df[int_cols] = df[int_cols].astype(int) 97 | 98 | df.drop('graph', axis=1, inplace=True) 99 | 100 | return df 101 | 102 | def _parse_row_with_link(row): 103 | """ 104 | Take in a tr tag and get the data out of it in the form of a list of 105 | strings, also get link. 106 | """ 107 | # parse the row like before and save it into a list 108 | parsed_row = [str(x.string) for x in row.find_all('td')] 109 | 110 | # now get the link, which is the href attribute of the first 'a' tag 111 | link = ffc_base_url + str(row.find_all('a')[0].get('href')) 112 | 113 | # add link onto the end of the list and return 114 | return parsed_row + [link] 115 | 116 | ############################################################################### 117 | # 5.1.3 118 | ############################################################################### 119 | def ffc_player_info(url): 120 | ffc_response = requests.get(url) 121 | player_soup = Soup(ffc_response.text) 122 | 123 | # info is in multiple tables, but can get all rows with shortcut 124 | rows = player_soup.find_all('tr') 125 | 126 | list_of_parsed_rows = [_parse_row(row) for row in rows] 127 | 128 | # this is a list of two item lists [[key1, value1], [key2, value2], ...], 129 | # so we're unpacking each key, value pair with for key, value in ... 130 | dict_of_parsed_rows = {key: value for key, value in list_of_parsed_rows} 131 | 132 | # now modify slightly to return what we want, which (per problem 133 | # instructions) is team, height, weight, birthday, and draft info 134 | return_dict = {} 135 | return_dict['team'] = dict_of_parsed_rows['Team:'] 136 | return_dict['height'] = dict_of_parsed_rows['Ht / Wt:'].split('/')[0] 137 | return_dict['weight'] = dict_of_parsed_rows['Ht / Wt:'].split('/')[1] 138 | return_dict['birthday'] = dict_of_parsed_rows['Born:'] 139 | return_dict['drafted'] = dict_of_parsed_rows['Drafted:'] 140 | return_dict['draft_team'] = dict_of_parsed_rows['Draft Team:'] 141 | 142 | return return_dict 143 | 144 | # note: we haven't talked about if __name__ == '__main__' 145 | # it' Python convention to put all your useful functions above this part, and 146 | # the code that actually calls it below 147 | # this way the test code doesn't get run if you import scrape_ffc to use in 148 | # another file 149 | # don't worry too much about this 150 | if __name__ == '__main__': 151 | # testing 5.1.1 152 | ppr_12_2016 = scrape_ffc('ppr', 12, 2016) 153 | std_12_2011 = scrape_ffc('std', 12, 2011) 154 | half_12_2018 = scrape_ffc('std', 12, 2018) 155 | 156 | # testing 5.1.2 157 | ppr_12_2015 = scrape_ffc_plus('ppr', 12, 2015) 158 | std_12_2012 = scrape_ffc_plus('std', 12, 2012) 159 | half_12_2019 = scrape_ffc_plus('std', 12, 2019) 160 | 161 | # testing 5.1.3 162 | # 'https://fantasyfootballcalculator.com/adp/players/adrian-peterson' 163 | test_link = ppr_12_2015['link'].iloc[0] 164 | ffc_player_info(test_link) 165 | -------------------------------------------------------------------------------- /solutions-to-exercises/06_plotting_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for Summary Stats and Visualization 3 | chapter. 4 | """ 5 | import pandas as pd 6 | import seaborn as sns 7 | import matplotlib.pyplot as plt 8 | from os import path 9 | 10 | # change this to the directory where the csv files that come with the book are 11 | # stored 12 | # on Windows it might be something like 'C:/mydir' 13 | 14 | # DATA_DIR = '/Users/nathan/fantasybook/data' 15 | DATA_DIR = './data' 16 | 17 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) # play by play data 18 | 19 | ############################################################################### 20 | # 6.1a 21 | ############################################################################### 22 | 23 | # Using the team game data, plot the distribution of three point attempts. Make 24 | # sure to give your plot a title. 25 | 26 | g = (sns.FacetGrid(dftg) 27 | .map(sns.kdeplot, 'fg3a', fill=True)) 28 | g.figure.subplots_adjust(top=0.9) 29 | g.figure.suptitle('Distribution of 3 Pt Attempts') 30 | g.savefig('./solutions-to-exercises/6-1a.png') 31 | 32 | # Now modify your plot to show the distribution of three point attempts by 33 | # whether the team won. Do it (b) as separate colors on the same plot, and (c) 34 | # as separate plots. 35 | 36 | # 6.1b 37 | g = (sns.FacetGrid(dftg, hue='wl') 38 | .map(sns.kdeplot, 'fg3a', fill=True)) 39 | g.figure.subplots_adjust(top=0.9) 40 | g.figure.suptitle('Distribution of 3 Pt Attempts by Win/Loss B') 41 | g.savefig('./solutions-to-exercises/6-1b.png') 42 | 43 | # 6.1c 44 | g = (sns.FacetGrid(dftg, col='wl') 45 | .map(sns.kdeplot, 'fg3a', fill=True)) 46 | g.figure.subplots_adjust(top=0.8) 47 | g.figure.suptitle('Distribution of 3 Pt Attempts by Win/Loss C') 48 | g.savefig('./solutions-to-exercises/6-1c.png') 49 | 50 | # (d) Sometimes it's effective to use the multiple keywords ("levers") to 51 | # display redundant information, experiment with this. 52 | 53 | g = (sns.FacetGrid(dftg, col='wl', hue='wl') 54 | .map(sns.kdeplot, 'fg3a', fill=True)) 55 | g.figure.subplots_adjust(top=0.8) 56 | g.figure.suptitle('Distribution of 3 Pt Attempts by Win/Loss D') 57 | g.savefig('./solutions-to-exercises/6-1d.png') 58 | 59 | # (e) Plot the three point attempts by team, with each team is on it's own 60 | # plot. Make sure to limit the number of columns so your plot isn't just one 61 | # wide row. 62 | 63 | g = (sns.FacetGrid(dftg, col='team', col_wrap=6) 64 | .map(sns.kdeplot, 'fg3a', fill=True)) 65 | g.figure.subplots_adjust(top=0.9) 66 | g.figure.suptitle('Distribution of 3 Pt Attempts by Team') 67 | g.savefig('./solutions-to-exercises/6-1e.png') 68 | 69 | # #### 6.2 70 | # (a) Plot the relationship between three point attemtps and free throw 71 | # percentage. Again, make sure your plot has a title. 72 | 73 | g = sns.relplot(x='fg3a', y='ft_pct', data=dftg) 74 | g.figure.subplots_adjust(top=0.9) 75 | g.figure.suptitle('3 Pt Attempts vs. Free Throw %') 76 | g.savefig('./solutions-to-exercises/6-2a.png') 77 | 78 | # (b) Jitter three point attempts and run the scatter plot again. 79 | import random 80 | 81 | dftg['fg3a_jitter'] = dftg['fg3a'].apply(lambda x: x + random.gauss(0, 1)) 82 | dftg['ft_pct_jitter'] = dftg['ft_pct'].apply(lambda x: x + random.gauss(0, 0.01)) 83 | 84 | g = sns.relplot(x='fg3a_jitter', y='ft_pct_jitter', data=dftg) 85 | g.figure.subplots_adjust(top=0.9) 86 | g.figure.suptitle('3 Pt Attempts vs. Free Throw % Jittered') 87 | g.savefig('./solutions-to-exercises/6-2b.png') 88 | 89 | # (c) It's hard to tell whether this cloud of points is moving upward to the 90 | # right or not. Check the correlation between these two variables numerically. 91 | 92 | dftg[['fg3a', 'ft_pct']].corr() 93 | 94 | -------------------------------------------------------------------------------- /solutions-to-exercises/07_modeling_answers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Answers to the end of chapter exercises for Modeling chapter. 3 | """ 4 | import pandas as pd 5 | import random 6 | from pandas import DataFrame, Series 7 | import statsmodels.formula.api as smf 8 | from os import path 9 | 10 | DATA_DIR = './data' 11 | 12 | ############################################################################### 13 | # problem 7.1 14 | ############################################################################### 15 | 16 | ################### 17 | # from 07_01_ols.py 18 | ################### 19 | df = pd.read_csv(path.join(DATA_DIR, 'shots.csv')) 20 | 21 | df['dist_sq'] = df['dist']**2 22 | df['made'] = df['made'].astype(int) 23 | 24 | df[['made', 'dist', 'dist_sq']].head() 25 | 26 | model = smf.ols(formula='made ~ dist + dist_sq', data=df) 27 | results = model.fit() 28 | 29 | results.summary2() 30 | 31 | def prob_of_make(dist): 32 | b0, b1, b2 = results.params 33 | return (b0 + b1*dist + b2*(dist**2)) 34 | 35 | df['made_hat'] = results.predict(df) 36 | 37 | ######################### 38 | # answers to question 7.1 39 | ######################### 40 | # a 41 | df['made_hat_alt'] = df['dist'].apply(prob_of_make) 42 | 43 | df[['made_hat', 'made_hat_alt']].head() 44 | 45 | # check whether made_hat and made_hat_alt are within some epsilon 46 | 47 | (df['made_hat_alt'] == df['made_hat']).all() 48 | 49 | import numpy as np 50 | (np.abs(df['made_hat'] - df['made_hat_alt']) < .00000001).all() 51 | 52 | # b 53 | model_b = smf.ols( 54 | formula='made ~ dist + dist_sq + C(value)', data=df) 55 | results_b = model_b.fit() 56 | results_b.summary2() 57 | 58 | # c 59 | df['is3'] = df['value'] == 3 60 | 61 | model_d = smf.ols(formula='made ~ dist + dist_sq + is3', data=df) 62 | results_d = model_d.fit() 63 | results_d.summary2() 64 | 65 | ############################################################################### 66 | # problem 7.2 67 | ############################################################################### 68 | 69 | # a 70 | def run_sim_get_pvalue(): 71 | coin = ['H', 'T'] 72 | 73 | # make empty DataFrame 74 | df = DataFrame(index=range(100)) 75 | 76 | # now fill it with a "guess" 77 | df['guess'] = [random.choice(coin) for _ in range(100)] 78 | 79 | # and flip 80 | df['result'] = [random.choice(coin) for _ in range(100)] 81 | 82 | # did we get it right or not? 83 | df['right'] = (df['guess'] == df['result']).astype(int) 84 | 85 | model = smf.ols(formula='right ~ C(guess)', data=df) 86 | results = model.fit() 87 | 88 | return results.pvalues['C(guess)[T.T]'] 89 | 90 | # b 91 | sims_1k = Series([run_sim_get_pvalue() for _ in range(1000)]) 92 | sims_1k.mean() # 0.5083 93 | 94 | # c 95 | def runs_till_threshold(i, p=0.05): 96 | pvalue = run_sim_get_pvalue() 97 | if pvalue < p: 98 | return i 99 | else: 100 | return runs_till_threshold(i+1, p) 101 | 102 | sim_time_till_sig_100 = Series([runs_till_threshold(1) for _ in range(100)]) 103 | 104 | # d 105 | 106 | # According to Wikipedia, the mean and median of the Geometric distribution are 107 | # 1/p and -1/log_2(1-p). Since we're working with a p of 0.05, that'd give us: 108 | 109 | from math import log 110 | p = 0.05 111 | g_mean = 1/p # 20 112 | g_median = -1/log(1-p, 2) # 13.51 113 | 114 | g_mean, g_median 115 | 116 | sim_time_till_sig_100.mean() 117 | sim_time_till_sig_100.median() 118 | 119 | ############################################################################### 120 | # problem 7.3 121 | ############################################################################### 122 | 123 | dftg = pd.read_csv(path.join(DATA_DIR, 'team_games.csv')) 124 | 125 | # a) Run a logit model regressing three point percentage, offensive and defensive 126 | # rebounds, steals, turnovers and blocks on whether a team wins. 127 | 128 | dftg['win'] = (dftg['wl'] == 'W').astype(int) 129 | 130 | # a 131 | model_a = smf.logit(formula= 132 | """ 133 | win ~ fg3_pct + oreb + dreb + stl + tov + blk 134 | """, data=dftg) 135 | results_a = model_a.fit() 136 | results_a.summary2() 137 | 138 | margeff = results_a.get_margeff() 139 | margeff.summary() 140 | 141 | # b 142 | model_b = smf.logit(formula= 143 | """ 144 | win ~ fg3_pct + oreb + dreb + stl + tov + blk + C(team) 145 | """, data=dftg) 146 | results_b = model_b.fit() 147 | results_b.summary2() 148 | 149 | ############################################################################### 150 | # problem 7.4 151 | ############################################################################### 152 | from sklearn.ensemble import RandomForestClassifier 153 | from sklearn.model_selection import train_test_split, cross_val_score 154 | 155 | xvars = ['min', 'pts', 'fgm', 'fga', 'fg_pct', 'fg3m', 'fg3a', 'fg3_pct', 156 | 'ftm', 'fta', 'ft_pct', 'oreb', 'dreb', 'reb', 'ast', 'stl', 'blk', 'tov', 157 | 'pf', 'plus_minus', 'bubble', 'win'] 158 | 159 | yvar = 'team' 160 | 161 | model = RandomForestClassifier(n_estimators=100) 162 | 163 | scores = cross_val_score(model, dftg[xvars], dftg[yvar], cv=10) 164 | scores.mean() 165 | scores.min() 166 | scores.max() 167 | 168 | # d 169 | 1/30 # .033 with random guess 170 | 171 | # feature important on model 172 | model.fit(dftg[xvars], dftg[yvar]) # running model fitting on entire dataset 173 | Series(model.feature_importances_, xvars).sort_values(ascending=False) 174 | -------------------------------------------------------------------------------- /solutions-to-exercises/6-1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-1a.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-1b.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-1c.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-1d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-1d.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-1e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-1e.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-2.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-2a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-2a.png -------------------------------------------------------------------------------- /solutions-to-exercises/6-2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathanbraun/code-basketball-files/43fb449fe8c332c619e94cc0067f0f8308b9d84e/solutions-to-exercises/6-2b.png --------------------------------------------------------------------------------