Tout le monde à travers le pays aime le basket-ball, bonjour. Je m'appelle Hikoichi Aida. Je travaille généralement en tant que manager et data scientist dans une équipe de basket-ball d'un lycée, analysant diverses données.
Cette fois, j'aimerais analyser les statistiques des joueurs (résultats) de la NBA, qui est une ligue de basket-ball professionnelle aux États-Unis. C'est facile à analyser, mais veuillez rester en contact.
Le premier concerne le scraping et le prétraitement car il s'agit de la préparation des données. Je ne sais pas quand auront lieu la deuxième session et les suivantes, pardonnez-moi. Ce ne sera peut-être pas pour toujours.
J'ai utilisé Google Colaboratory. Le processus introduit cette fois ne peut être utilisé qu'avec la bibliothèque préinstallée. C'est très pratique.
Lorsque je cherchais quoi faire avec la partie collecte de données, j'ai trouvé l'article de blog suivant.
Cet article et le contenu sont presque les mêmes, mais j'ai pensé que la taille et le poids du joueur pourraient être grattés, j'ai donc inclus l'URL de la page personnelle du joueur comme cible de grattage. Étant donné que l'enregistrement représentant le nom de la colonne est inséré à intervalles réguliers, cet enregistrement est ignoré.
data = pd.DataFrame()
years = [i for i in range(2000, 2002)]
for year in years:
url = "https://www.basketball-reference.com/leagues/NBA_{}_per_game.html".format(year)
# this is the HTML from the given URL
html = urlopen(url)
soup = BeautifulSoup(html)
soup.findAll('tr', limit=2)
# use getText()to extract the text we need into a list
headers = [th.getText() for th in soup.findAll('tr', limit=2)[0].findAll('th')]
# exclude the first column as we will not need the ranking order from Basketball Reference for the analysis
headers = ['URL'] + headers[1:] + ['Year']
rows = soup.findAll('tr')[1:]
player_stats = [[rows[i].a.get('href')] + [td.getText() for td in rows[i].findAll('td')] for i in range(len(rows)) if (rows[i].findAll('td')) and (rows[i].a)]
stats = pd.DataFrame(player_stats)
stats['Year'] = str(year)
stats.columns = headers
data = pd.concat([data, stats])
data = data.dropna()
Voici un exemple de page à gratter.
Toutes les statistiques des joueurs qui ont participé à cette saison sont sur le site d'une page, donc je pense que vous pouvez collecter suffisamment de données même si vous les faites glisser et les copiez dans un logiciel de calcul de table tel qu'Excel.
Cette fois, nous avons ciblé des données sur 20 ans (2000-2019). Le résultat du grattage ressemble à ceci.
URL | Player | Pos | Age | Tm | G | GS | MP | FG | FGA | FG% | 3P | 3PA | 3P% | 2P | 2PA | 2P% | eFG% | FT | FTA | FT% | ORB | DRB | TRB | AST | STL | BLK | TOV | PF | PTS | Year | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | /players/a/abdulta01.html | Tariq Abdul-Wahad | SG | 25 | TOT | 61 | 56 | 25.9 | 4.5 | 10.6 | .424 | 0.0 | 0.4 | .130 | 4.4 | 10.2 | .435 | .426 | 2.4 | 3.2 | .756 | 1.7 | 3.1 | 4.8 | 1.6 | 1.0 | 0.5 | 1.7 | 2.4 | 11.4 | 2000 |
1 | /players/a/abdulta01.html | Tariq Abdul-Wahad | SG | 25 | ORL | 46 | 46 | 26.2 | 4.8 | 11.2 | .433 | 0.0 | 0.5 | .095 | 4.8 | 10.7 | .447 | .435 | 2.5 | 3.3 | .762 | 1.7 | 3.5 | 5.2 | 1.6 | 1.2 | 0.3 | 1.9 | 2.5 | 12.2 | 2000 |
2 | /players/a/abdulta01.html | Tariq Abdul-Wahad | SG | 25 | DEN | 15 | 10 | 24.9 | 3.4 | 8.7 | .389 | 0.1 | 0.1 | .500 | 3.3 | 8.6 | .388 | .393 | 2.1 | 2.8 | .738 | 1.6 | 1.9 | 3.5 | 1.7 | 0.4 | 0.8 | 1.3 | 2.1 | 8.9 | 2000 |
3 | /players/a/abdursh01.html | Shareef Abdur-Rahim | SF | 23 | VAN | 82 | 82 | 39.3 | 7.2 | 15.6 | .465 | 0.4 | 1.2 | .302 | 6.9 | 14.4 | .478 | .477 | 5.4 | 6.7 | .809 | 2.7 | 7.4 | 10.1 | 3.3 | 1.1 | 1.1 | 3.0 | 3.0 | 20.3 | 2000 |
4 | /players/a/alexaco01.html | Cory Alexander | PG | 26 | DEN | 29 | 2 | 11.3 | 1.0 | 3.4 | .286 | 0.3 | 1.2 | .257 | 0.7 | 2.2 | .302 | .332 | 0.6 | 0.8 | .773 | 0.3 | 1.2 | 1.4 | 2.0 | 0.8 | 0.1 | 1.0 | 1.3 | 2.8 | 2000 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
703 | /players/z/zellety01.html | Tyler Zeller | C | 29 | MEM | 4 | 1 | 20.5 | 4.0 | 7.0 | .571 | 0.0 | 0.0 | 4.0 | 7.0 | .571 | .571 | 3.5 | 4.5 | .778 | 2.3 | 2.3 | 4.5 | 0.8 | 0.3 | 0.8 | 1.0 | 4.0 | 11.5 | 2019 | |
704 | /players/z/zizican01.html | Ante Žižić | C | 22 | CLE | 59 | 25 | 18.3 | 3.1 | 5.6 | .553 | 0.0 | 0.0 | 3.1 | 5.6 | .553 | .553 | 1.6 | 2.2 | .705 | 1.8 | 3.6 | 5.4 | 0.9 | 0.2 | 0.4 | 1.0 | 1.9 | 7.8 | 2019 | |
705 | /players/z/zubaciv01.html | Ivica Zubac | C | 21 | TOT | 59 | 37 | 17.6 | 3.6 | 6.4 | .559 | 0.0 | 0.0 | 3.6 | 6.4 | .559 | .559 | 1.7 | 2.1 | .802 | 1.9 | 4.2 | 6.1 | 1.1 | 0.2 | 0.9 | 1.2 | 2.3 | 8.9 | 2019 | |
706 | /players/z/zubaciv01.html | Ivica Zubac | C | 21 | LAL | 33 | 12 | 15.6 | 3.4 | 5.8 | .580 | 0.0 | 0.0 | 3.4 | 5.8 | .580 | .580 | 1.7 | 2.0 | .864 | 1.6 | 3.3 | 4.9 | 0.8 | 0.1 | 0.8 | 1.0 | 2.2 | 8.5 | 2019 | |
707 | /players/z/zubaciv01.html | Ivica Zubac | C | 21 | LAC | 26 | 25 | 20.2 | 3.8 | 7.2 | .538 | 0.0 | 0.0 | 3.8 | 7.2 | .538 | .538 | 1.7 | 2.3 | .733 | 2.3 | 5.3 | 7.7 | 1.5 | 0.4 | 0.9 | 1.4 | 2.5 | 9.4 | 2019 |
Les statistiques qui représentent la probabilité telle que FG% (pensez taux de réussite des buts sur le terrain = taux de réussite des tirs) semblent être vides si le nombre de tirs d'essai est de 0. Remplacez par NaN.
data = data.replace(r'^\s*$', np.NaN, regex=True)
Le type de données est une chaîne de caractères. Convertissez les données que vous souhaitez traiter comme un nombre en flottant. Il y a des données avec seulement des entiers, mais comme c'est gênant, je vais tout faire flotter. Avant le changement, la note exprimée en pourcentage est écrite sous la forme «.XXX», et elle ne peut pas être convertie en un nombre tel quel, alors ajoutez «0» au début.
add_zero_cols = [col for col in data.columns if '%' in col]
num_cols = ['Age'] + list(data.columns[5:-1])
for col in add_zero_cols:
data[col] = '0' + data[col]
for col in num_cols:
data[col] = data[col].astype(float)
Allons vérifier. Affiche les 10 meilleurs scores moyens.
data.sort_values('PTS', ascending=False)[['Player', 'PTS', 'Year']].head(10)
Player | PTS | Year | |
---|---|---|---|
11135 | James Harden | 36.1 | 2019 |
3266 | Kobe Bryant* | 35.4 | 2006 |
3428 | Allen Iverson* | 33.0 | 2006 |
1813 | Tracy McGrady* | 32.1 | 2003 |
7954 | Kevin Durant | 32.0 | 2014 |
3818 | Kobe Bryant* | 31.6 | 2007 |
10167 | Russell Westbrook | 31.6 | 2017 |
1249 | Allen Iverson* | 31.4 | 2002 |
3442 | LeBron James | 31.4 | 2006 |
715 | Allen Iverson* | 31.1 | 2001 |
Il ne semble y avoir aucun problème. Il existe de nombreuses superstars bien connues au Japon telles que James Harden, Allen Iverson et Corby Bryant.
J'ai jeté un coup d'œil sur la page de la source de données pour voir ce que la marque * après le nom représente, mais je n'étais pas sûr: sweat_smile: Il peut représenter un joueur qui est dans le Hall of Fame.
Nous avons également collecté des données sur la taille et le poids à partir de l'URL de la page personnelle qui était également incluse dans l'élément de grattage. (Comme il peut être collecté en modifiant légèrement le code au début, le code des données supplémentaires est omis)
Enfin, ces données sont prêtes. (Les colonnes Poids et Hauteur ont été ajoutées à l'extrême droite)
Player | Pos | Age | Tm | G | GS | MP | FG | FGA | FG% | 3P | 3PA | 3P% | 2P | 2PA | 2P% | eFG% | FT | FTA | FT% | ORB | DRB | TRB | AST | STL | BLK | TOV | PF | PTS | Year | Weight | Height | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Tariq Abdul-Wahad | SG | 25.0 | TOT | 61.0 | 56.0 | 25.9 | 4.5 | 10.6 | 0.424 | 0.0 | 0.4 | 0.130 | 4.4 | 10.2 | 0.435 | 0.426 | 2.4 | 3.2 | 0.756 | 1.7 | 3.1 | 4.8 | 1.6 | 1.0 | 0.5 | 1.7 | 2.4 | 11.4 | 2000 | 101.24 | 1.98 |
3 | Shareef Abdur-Rahim | SF | 23.0 | VAN | 82.0 | 82.0 | 39.3 | 7.2 | 15.6 | 0.465 | 0.4 | 1.2 | 0.302 | 6.9 | 14.4 | 0.478 | 0.477 | 5.4 | 6.7 | 0.809 | 2.7 | 7.4 | 10.1 | 3.3 | 1.1 | 1.1 | 3.0 | 3.0 | 20.3 | 2000 | 102.15 | 2.06 |
5 | Ray Allen* | SG | 24.0 | MIL | 82.0 | 82.0 | 37.4 | 7.8 | 17.2 | 0.455 | 2.1 | 5.0 | 0.423 | 5.7 | 12.2 | 0.468 | 0.516 | 4.3 | 4.9 | 0.887 | 1.0 | 3.4 | 4.4 | 3.8 | 1.3 | 0.2 | 2.2 | 2.3 | 22.1 | 2000 | 93.07 | 1.96 |
7 | John Amaechi | C | 29.0 | ORL | 80.0 | 53.0 | 21.1 | 3.8 | 8.8 | 0.437 | 0.0 | 0.1 | 0.167 | 3.8 | 8.7 | 0.439 | 0.438 | 2.8 | 3.6 | 0.766 | 0.8 | 2.6 | 3.3 | 1.2 | 0.4 | 0.5 | 1.7 | 2.0 | 10.5 | 2000 | 122.58 | 2.08 |
8 | Derek Anderson | SG | 25.0 | LAC | 64.0 | 58.0 | 34.4 | 5.9 | 13.4 | 0.438 | 0.9 | 2.8 | 0.309 | 5.0 | 10.7 | 0.472 | 0.470 | 4.2 | 4.8 | 0.877 | 1.3 | 2.8 | 4.0 | 3.4 | 1.4 | 0.2 | 2.6 | 2.3 | 16.9 | 2000 | 88.08 | 1.96 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
11561 | Delon Wright | PG | 26.0 | TOT | 75.0 | 13.0 | 22.7 | 3.2 | 7.4 | 0.434 | 0.7 | 2.2 | 0.298 | 2.6 | 5.2 | 0.492 | 0.478 | 1.6 | 2.0 | 0.793 | 0.9 | 2.6 | 3.5 | 3.3 | 1.2 | 0.4 | 1.0 | 1.4 | 8.7 | 2019 | 83.08 | 1.96 |
11566 | Thaddeus Young | PF | 30.0 | IND | 81.0 | 81.0 | 30.7 | 5.5 | 10.4 | 0.527 | 0.6 | 1.8 | 0.349 | 4.8 | 8.6 | 0.564 | 0.557 | 1.1 | 1.7 | 0.644 | 2.4 | 4.1 | 6.5 | 2.5 | 1.5 | 0.4 | 1.5 | 2.4 | 12.6 | 2019 | 99.88 | 2.03 |
11567 | Trae Young | PG | 20.0 | ATL | 81.0 | 81.0 | 30.9 | 6.5 | 15.5 | 0.418 | 1.9 | 6.0 | 0.324 | 4.6 | 9.6 | 0.477 | 0.480 | 4.2 | 5.1 | 0.829 | 0.8 | 2.9 | 3.7 | 8.1 | 0.9 | 0.2 | 3.8 | 1.7 | 19.1 | 2019 | 81.72 | 1.86 |
11572 | Ante Žižić | C | 22.0 | CLE | 59.0 | 25.0 | 18.3 | 3.1 | 5.6 | 0.553 | 0.0 | 0.0 | NaN | 3.1 | 5.6 | 0.553 | 0.553 | 1.6 | 2.2 | 0.705 | 1.8 | 3.6 | 5.4 | 0.9 | 0.2 | 0.4 | 1.0 | 1.9 | 7.8 | 2019 | 115.32 | 2.08 |
11573 | Ivica Zubac | C | 21.0 | TOT | 59.0 | 37.0 | 17.6 | 3.6 | 6.4 | 0.559 | 0.0 | 0.0 | NaN | 3.6 | 6.4 | 0.559 | 0.559 | 1.7 | 2.1 | 0.802 | 1.9 | 4.2 | 6.1 | 1.1 | 0.2 | 0.9 | 1.2 | 2.3 | 8.9 | 2019 | 108.96 | 2.13 |
Au fait, les 10 plus hauts sommets des 20 dernières années
df.groupby(['Player']).max().reset_index().sort_values('Height', ascending=False)[['Player', 'Year', 'Height', 'Weight']].head(10)
Player | Year | Height | Weight | |
---|---|---|---|---|
672 | Gheorghe Mureșan | 2000 | 2.31 | 137.56 |
1641 | Shawn Bradley | 2005 | 2.29 | 106.69 |
1890 | Yao Ming* | 2011 | 2.29 | 140.74 |
1653 | Sim Bhullar | 2015 | 2.26 | 163.44 |
1442 | Pavel Podkolzin | 2006 | 2.26 | 118.04 |
1656 | Slavko Vraneš | 2004 | 2.26 | 124.85 |
1519 | Rik Smits | 2000 | 2.24 | 113.50 |
172 | Boban Marjanović | 2019 | 2.24 | 131.66 |
1449 | Peter John Ramos | 2005 | 2.21 | 124.85 |
583 | Edy Tavares | 2017 | 2.21 | 118.04 |
L'unité est le m (mètre). La troisième place est celle de Yao Min, qui s'appelait aussi la Grande Muraille de Mari. La hauteur est trop grande.
Maintenant que les données sont prêtes, j'aimerais les visualiser la prochaine fois.
data.sort_values('AST', ascending=False)[['Player', 'AST', 'Year']].head(10)
Player | AST | Year | |
---|---|---|---|
6617 | Deron Williams | 12.8 | 2011 |
7061 | Rajon Rondo | 11.7 | 2012 |
9486 | Rajon Rondo | 11.7 | 2016 |
4085 | Steve Nash* | 11.6 | 2007 |
4686 | Chris Paul | 11.6 | 2008 |
2977 | Steve Nash* | 11.5 | 2005 |
6440 | Steve Nash* | 11.4 | 2011 |
6509 | Rajon Rondo | 11.2 | 2011 |
9819 | James Harden | 11.2 | 2017 |
7641 | Rajon Rondo | 11.1 | 2013 |
data.sort_values('TRB', ascending=False)[['Player', 'TRB', 'Year']].head(10)
Player | TRB | Year | |
---|---|---|---|
7236 | Earl Barron | 18.0 | 2013 |
651 | Danny Fortson | 16.3 | 2001 |
10372 | Andre Drummond | 16.0 | 2018 |
11056 | Andre Drummond | 15.6 | 2019 |
1974 | Ben Wallace | 15.4 | 2003 |
6391 | Kevin Love | 15.2 | 2011 |
10537 | DeAndre Jordan | 15.2 | 2018 |
8695 | DeAndre Jordan | 15.0 | 2015 |
9163 | Andre Drummond | 14.8 | 2016 |
6894 | Dwight Howard | 14.5 | 2012 |
Il semble que les joueurs n'ayant pas atteint le nombre de parties spécifié doivent être exclus.
Recommended Posts