Prédisez les courses de chevaux avec l'apprentissage automatique et visez un taux de récupération de 100%.
La dernière fois, j'ai créé un modèle d'apprentissage automatique qui prédit les chevaux qui seront dans le top 3 avec LightGBM. Cette fois, j'aimerais ajouter "performances passées des chevaux" comme quantité de caractéristiques, mais quand j'essaye de le faire, le grattage et le traitement des données sont assez difficiles. Donc, je voudrais résumer quel type de code doit être écrit et implémenté </ font>.
Tout d'abord, retrouvez les résultats passés de tous les chevaux en course en 2019 sur netkeiba.com. Sur netkeiba.com, horse_id est donné pour chaque cheval et l'URL de la page des résultats passés est 「https://db.netkeiba.com/horse/(horse_id)」 Puisqu'elle a la structure, la fonction scrape_race_results créée dans Article précédent est traitée pour gratter le horse_id nécessaire (ainsi que l'identifiant du cavalier).
import time
from tqdm.notebook import tqdm
import requests
from bs4 import BeautifulSoup
import re
import pandas as pd
def scrape_race_results(race_id_list, pre_race_results={}):
race_results = pre_race_results
for race_id in tqdm(race_id_list):
if race_id in race_results.keys():
continue
try:
url = "https://db.netkeiba.com/race/" + race_id
df = pd.read_html(url)[0]
# horse_id et jockey_Identifiant de raclage
html = requests.get(url)
html.encoding = "EUC-JP"
soup = BeautifulSoup(html.text, "html.parser")
# horse_id
horse_id_list = []
horse_a_list = soup.find("table", attrs={"summary": "Résultat de la course"}).find_all(
"a", attrs={"href": re.compile("^/horse")}
)
for a in horse_a_list:
horse_id = re.findall(r"\d+", a["href"])
#Il est en majuscule car il provoque un bogue lors de l'utilisation de la barre oblique inverse dans qiita.
horse_id_list.append(horse_id[0])
# jockey_id
jockey_id_list = []
jockey_a_list = soup.find("table", attrs={"summary": "Résultat de la course"}).find_all(
"a", attrs={"href": re.compile("^/jockey")}
)
for a in jockey_a_list:
jockey_id = re.findall(r"\d+", a["href"])
jockey_id_list.append(jockey_id[0])
df["horse_id"] = horse_id_list
df["jockey_id"] = jockey_id_list
race_results[race_id] = df
time.sleep(1)
except IndexError:
continue
except Exception as e:
print(e)
break
return race_results
Convertissez en type DataFrame en référence à l'article précédent. Cela vous donnera une liste des horse_ids dont vous avez besoin.
results = scrape_race_results(race_id_list)
results = pd.concat([results[key] for key in results])
horse_id_list = results['horse_id'].unique()
Ceci est utilisé pour récupérer les données de performances passées.
def scrape_horse_results(horse_id_list, pre_horse_id=[]):
horse_results = {}
for horse_id in tqdm(horse_id_list):
if horse_id in pre_horse_id:
continue
try:
url = 'https://db.netkeiba.com/horse/' + horse_id
df = pd.read_html(url)[3]
if df.columns[0]=='Historique des récompenses':
df = pd.read_html(url)[4]
horse_results[horse_id] = df
time.sleep(1)
except IndexError:
continue
except Exception as e:
import traceback
traceback.print_exc()
print(e)
break
except:
break
return horse_results
Cela prend beaucoup de temps, mais après le scraping, faites-en à nouveau un type DataFrame et enregistrez-le dans un fichier pickle.
horse_results = scrape_horse_results(horse_id_list)
for key in horse_results:
horse_results[key].index = [key] * len(horse_results[key])
df = pd.concat([horse_results[key] for key in horse_results])
df.to_pickle('horse_results.pickle')
Ensuite, créez une classe appelée HorseResults et implémentez une fonction qui fusionne l'ordre d'arrivée et la moyenne des prix.
class HorseResults:
def __init__(self, horse_results):
self.horse_results = horse_results[['Date', 'Ordre d'arrivée', 'Prix']]
self.preprocessing()
def preprocessing(self):
df = self.horse_results.copy()
#Supprimer les éléments contenant des chaînes de caractères non numériques dans l'ordre d'arrivée
df['Ordre d'arrivée'] = pd.to_numeric(df['Ordre d'arrivée'], errors='coerce')
df.dropna(subset=['Ordre d'arrivée'], inplace=True)
df['Ordre d'arrivée'] = df['Ordre d'arrivée'].astype(int)
df["date"] = pd.to_datetime(df["Date"])
df.drop(['Date'], axis=1, inplace=True)
#Remplissez le prix NaN avec 0
df['Prix'].fillna(0, inplace=True)
self.horse_results = df
def average(self, horse_id_list, date, n_samples='all'):
target_df = self.horse_results.loc[horse_id_list]
#Spécifiez le nombre d'exécutions dans le passé
if n_samples == 'all':
filtered_df = target_df[target_df['date'] < date]
elif n_samples > 0:
filtered_df = target_df[target_df['date'] < date].\
sort_values('date', ascending=False).groupby(level=0).head(n_samples)
else:
raise Exception('n_samples must be >0')
average = filtered_df.groupby(level=0)[['Ordre d'arrivée', 'Prix']].mean()
return average.rename(columns={'Ordre d'arrivée':'Ordre d'arrivée_{}R'.format(n_samples), 'Prix':'Prix_{}R'.format(n_samples)})
def merge(self, results, date, n_samples='all'):
df = results[results['date']==date]
horse_id_list = df['horse_id']
merged_df = df.merge(self.average(horse_id_list, date, n_samples), left_on='horse_id',
right_index=True, how='left')
return merged_df
def merge_all(self, results, n_samples='all'):
date_list = results['date'].unique()
merged_df = pd.concat([self.merge(results, date, n_samples) for date in tqdm(date_list)])
return merged_df
Avec cela, par exemple, si vous souhaitez ajouter les résultats des 5 dernières courses à la quantité de caractéristiques, vous pouvez implémenter comme suit.
hr = HorseResults(horse_results)
results_5R = hr.merge_all(results_p, n_samples=5)
Vous pouvez maintenant voir que l'ordre d'arrivée et la moyenne des 5 dernières courses de prix ont été ajoutés aux deux colonnes les plus à droite.
Les détails sont expliqués dans la vidéo ↓ Analyse de données / apprentissage automatique à partir de la prédiction des courses de chevaux
Recommended Posts