def generate_error_layout(year_id, circuit_id, driver_id): """ Generates an error layout in the event that there was no race in the given year at the given circuit or the given driver didn't compete in the given race. :param year_id: Year :param circuit_id: Circuit ID :param driver_id: Driver ID :return: Div layout """ logging.info("Generating error layout") driver_name = get_driver_name(driver_id) circuit_name = get_circuit_name(circuit_id) driver_results = results[results["driverId"] == driver_id] rids_driver_competed_in = driver_results["raceId"].values.tolist() driver_races = races.loc[rids_driver_competed_in] cids_driver_competed_at = driver_races["circuitId"].unique().tolist() # Generate the text text = f"Unfortunately, {driver_name} did not compete in a race at {circuit_name} in {year_id}. The driver " \ f"competed at the following tracks in the following years:<br>" text += "<ul>" for circuit_id in cids_driver_competed_at: years = driver_races[driver_races["circuitId"] == circuit_id]["year"].unique().tolist() years_str = rounds_to_str(years) text += f"<li>{get_circuit_name(circuit_id)} ({years_str})</li>" text += "</ul><br>" layout = Div(text=text) return layout
def generate_stats_layout(circuit_id, circuit_years, circuit_fastest_lap_data, circuit_results, circuit_races, download_image=True): """ Generates a layout of the circuit image along with some basic stats on the track :param circuit_id: Circuit ID :param circuit_years: Circuit years :param circuit_fastest_lap_data: Circuit fastest lap data :param circuit_results: Circuit results :param circuit_races: Circuit races :param download_image: Whether to download the image :return: Race stats layout """ logging.info("Generating circuit stats layout") circuit_row = circuits.loc[circuit_id] # Track image if download_image: image_url = str(circuit_row["imgUrl"]) image_view = plot_image_url(image_url) disclaimer = Div(text="The image is of the current configuration of the track.") image_view = column([image_view, disclaimer], sizing_mode="stretch_both") else: image_view = Div() # Circuit stats header_template = """ <h2 style="text-align: center;"><b>{}</b></h2> """ template = """ <pre><b>{}</b> {}<br></pre> """ circuit_stats = header_template.format("Circuit Stats") location = circuit_row["location"] + ", " + circuit_row["country"] circuit_stats += template.format("Location: ".ljust(22), location) years_races = rounds_to_str(circuit_years) if len(years_races) > 50: years_races = years_races[:50] + "<br>" + "".ljust(22) + years_races[50:] circuit_stats += template.format("Years Raced: ".ljust(22), years_races) num_races = circuit_races.shape[0] circuit_stats += template.format("Races Held: ".ljust(22), str(num_races)) avg_rating = circuit_row["avgRating"] if not np.isnan(avg_rating): circuit_stats += template.format("Avg. Fan Rating: ".ljust(22), round(avg_rating, 1)) # Fastest race lap if circuit_fastest_lap_data["fastest_lap_time_millis"].shape[0] > 0 and \ circuit_fastest_lap_data["fastest_lap_time_millis"].isna().sum() < \ circuit_fastest_lap_data["fastest_lap_time_millis"].shape[0]: minidx = circuit_fastest_lap_data["fastest_lap_time_millis"].idxmin() fastest = circuit_fastest_lap_data.loc[minidx] did = fastest["driver_id"] rid = fastest["raceId"] year = str(circuit_races.loc[rid, "year"]) cid = circuit_results[(circuit_results["raceId"] == rid) & (circuit_results["driverId"] == did)]["constructorId"].values[0] constructor = get_constructor_name(cid, include_flag=False) fastest = fastest["fastest_lap_time_str"] + " (" + get_driver_name(did) + ", " + constructor + ", " + year + ")" circuit_stats += template.format("Fastest Race Lap: ".ljust(22), fastest) # DNF pct if circuit_results.shape[0] > 0: classifications = circuit_results["statusId"].apply(get_status_classification) dnfs = classifications[(classifications == "mechanical") | (classifications == "crash")].shape[0] finishes = classifications[classifications == "finished"].shape[0] dnf_pct = dnfs / (dnfs + finishes) dnf_pct_str = f"{round(100 * dnf_pct, 1)}% of cars DNF'd" circuit_stats += template.format("DNF Percent: ".ljust(22), dnf_pct_str) # Weather and SC Laps weather = circuit_races["weather"].value_counts() if weather.shape[0] > 0: dry = weather["dry"] if "dry" in weather.index else 0 varied = weather["varied"] if "varied" in weather.index else 0 wet = weather["wet"] if "wet" in weather.index else 0 circuit_stats += "<i>Note, we only have safety car and weather data from 2007 to 2017</i>" circuit_stats += template.format("Dry Races: ".ljust(22), str(dry) + " (" + str(round(100 * dry / num_races, 1)) + "% of races)") circuit_stats += template.format("Varied Races: ".ljust(22), str(varied) + " (" + str(round(100 * varied / num_races, 1)) + "% of races)") circuit_stats += template.format("Wet Races: ".ljust(22), str(wet) + " (" + str(round(100 * wet / num_races, 1)) + "% of races)") sc_laps = circuit_races["SCLaps"].mean() if not np.isnan(sc_laps): circuit_stats += template.format("Avg. Safety Car Laps: ".ljust(22), round(sc_laps, 1)) stats_div = Div(text=circuit_stats) divider = vdivider() return row([image_view, divider, stats_div], sizing_mode="stretch_width")
def generate_drivers_table(): """ Table of all drivers showing: Name Number of races Years raced Number of WDC wins Number of race wins Number of podiums Constructors raced for :return: Drivers table layout """ logging.info("Generating drivers table") dids = drivers.index.values source = pd.DataFrame(columns=["driver_name", "num_races", "years", "num_wdc", "num_wins", "num_podiums", "constructors"]) for did in dids: driver_name = get_driver_name(did) driver_results = results[(results["driverId"] == did) & (results["grid"] > 0)] num_races = driver_results.shape[0] if num_races == 0: continue years = races.loc[driver_results["raceId"].values]["year"].unique() years.sort() years = rounds_to_str(years) num_wins = driver_results[driver_results["position"] == 1].shape[0] num_podiums = num_wins + driver_results[(driver_results["position"] == 2) | (driver_results["position"] == 3)].shape[0] wdc_wins = wdc_final_positions[(wdc_final_positions["driverId"] == did) & (wdc_final_positions["position"] == 1)] if wdc_wins.shape[0] == 0: num_wdc = str(wdc_wins.shape[0]) else: wdc_wins_years = wdc_wins["year"].unique() wdc_wins_years.sort() num_wdc = str(wdc_wins.shape[0]) + " (" + rounds_to_str(wdc_wins_years) + ")" constructors = ", ".join(list(map(get_constructor_name, driver_results["constructorId"].unique()))) source = source.append({ "driver_name": driver_name, "num_races": num_races, "years": years, "num_wdc": num_wdc, "num_wins": num_wins, "num_podiums": num_podiums, "constructors": constructors }, ignore_index=True) source = source.sort_values(by=["num_wdc", "num_wins", "num_podiums", "num_races"], ascending=False) drivers_columns = [ TableColumn(field="driver_name", title="Driver", width=60), TableColumn(field="num_races", title="Races", width=40), TableColumn(field="years", title="Years", width=85), TableColumn(field="num_wdc", title="WDC Wins", width=75), TableColumn(field="num_wins", title="Wins", width=40), TableColumn(field="num_podiums", title="Podiums", width=40), TableColumn(field="constructors", title="Constructors", width=130) ] drivers_table = DataTable(source=ColumnDataSource(data=source), columns=drivers_columns, index_position=None, min_height=530) title = Div(text=f"<h2><b>All Drivers</b></h2>") return column([title, drivers_table], sizing_mode="stretch_width")
def generate_stats_layout(cd_years, cd_races, cd_results, cd_fastest_lap_data, positions_source, circuit_id, driver_id, constructor_id=None): """ Stats div including: - Years - Num. races - Num. wins - Num. podiums - Best results - Average start position - Average finish position - Average lap time - Fastest lap time - Num mechanical DNFs and mechanical DNF rate - Num crash DNFs and crash DNF rate :param cd_years: CD years :param cd_races: CD races :param cd_results: CD results :param cd_fastest_lap_data: CD fastest lap data :param positions_source: Positions source :param driver_id: Driver ID :param circuit_id: Circuit ID :param constructor_id: If set to anything but None, will do constructor mode :return: Stats div layout """ logging.info("Generating stats div") num_races = cd_results.shape[0] if num_races == 0: return Div() win_results = cd_results[cd_results["positionOrder"] == 1] num_wins = win_results.shape[0] if num_wins > 0: rids = win_results["raceId"] years = sorted(cd_races.loc[rids.values, "year"].astype(str).values.tolist(), reverse=True) num_wins = str(num_wins) + " (" + ", ".join(years) + ")" else: num_wins = str(num_wins) podium_results = cd_results[cd_results["positionOrder"] <= 3] num_podiums = podium_results.shape[0] if num_podiums > 0: rids = podium_results["raceId"] years = list(set(cd_races.loc[rids.values, "year"].values.tolist())) years = rounds_to_str(years) num_podiums_str = str(num_podiums) + " (" + years + ")" if len(num_podiums_str) > 120: split = num_podiums_str.split(" ") split.insert(int(len(split) / 2), "<br> " + "".ljust(20)) num_podiums_str = " ".join(split) else: num_podiums_str = str(num_podiums) best_result = None if num_wins == 0: idxmin = cd_results["positionOrder"].idxmin() if not np.isnan(idxmin): rid = cd_results.loc[idxmin, "raceId"] year = cd_races.loc[rid, "year"] best_result = int_to_ordinal( int(cd_results.loc[idxmin, "positionOrder"])) + f" ({year})" mean_sp = round(cd_results["grid"].mean(), 1) mean_fp = round(cd_results["positionOrder"].mean(), 1) avg_lap_time = cd_fastest_lap_data["avg_lap_time_millis"].mean() fastest_lap_time = cd_fastest_lap_data["fastest_lap_time_millis"].min() classifications = cd_results["statusId"].apply(get_status_classification) num_mechanical_dnfs = classifications[classifications == "mechanical"].shape[0] num_crash_dnfs = classifications[classifications == "crash"].shape[0] num_finishes = classifications[classifications == "finished"].shape[0] mechanical_dnfs_str = str(num_mechanical_dnfs) crash_dnfs_str = str(num_crash_dnfs) finishes_str = str(num_finishes) if num_races > 0: mechanical_dnfs_str += " (" + str( round(100 * num_mechanical_dnfs / num_races, 1)) + "%)" crash_dnfs_str += " (" + str(round(100 * num_crash_dnfs / num_races, 1)) + "%)" finishes_str += " (" + str(round(100 * num_finishes / num_races, 1)) + "%)" if positions_source.shape[0] > 0: avg_finish_pos_overall = positions_source["avg_finish_pos"].mean() avg_finish_pos_here = positions_source["finish_position_int"].mean() diff = avg_finish_pos_here - avg_finish_pos_overall avg_finish_pos_overall = round(avg_finish_pos_overall, 1) avg_finish_pos_here = round(avg_finish_pos_here, 1) w = "higher" if diff < 0 else "lower" finish_pos_diff_str = f"Finished on average {round(abs(diff), 1)} place(s) {w} than average " \ f"(pos. {avg_finish_pos_here} here vs pos. {avg_finish_pos_overall} average overall)" else: finish_pos_diff_str = "" header_template = """ <h2 style="text-align: center;"><b>{}</b></h2> """ template = """ <pre><b>{}</b> {}<br></pre> """ if constructor_id: name = get_constructor_name(constructor_id, include_flag=False) else: name = get_driver_name(driver_id, include_flag=False, just_last=True) cd_stats = header_template.format( f"{name} at {get_circuit_name(circuit_id, include_flag=False)} Stats") cd_stats += template.format("Years: ".ljust(22), rounds_to_str(cd_years)) cd_stats += template.format("Num Races: ".ljust(22), str(num_races)) cd_stats += template.format("Num Wins: ".ljust(22), str(num_wins)) cd_stats += template.format("Num Podiums: ".ljust(22), str(num_podiums_str)) if best_result: cd_stats += template.format("Best Result: ".ljust(22), str(best_result)) cd_stats += template.format("Avg. Start Pos.: ".ljust(22), mean_sp) cd_stats += template.format("Avg. Finish Pos.: ".ljust(22), mean_fp) if not np.isnan(avg_lap_time): cd_stats += template.format("Avg. Lap Time: ".ljust(22), millis_to_str(avg_lap_time)) cd_stats += template.format("Fastest Lap Time: ".ljust(22), millis_to_str(fastest_lap_time)) cd_stats += template.format("Num. Mechanical DNFs: ".ljust(22), mechanical_dnfs_str) cd_stats += template.format("Num. Crash DNFs: ".ljust(22), crash_dnfs_str) cd_stats += template.format("Num Finishes".ljust(22), finishes_str) if positions_source.shape[0] > 0: cd_stats += template.format("Compared to Average: ".ljust(22), finish_pos_diff_str) return Div(text=cd_stats)
def generate_circuits_table(): """ Generates a table with information on every circuit. :return: Circuits table layout """ source = pd.DataFrame(columns=[ "circuit_name", "location", "num_races", "years", "top_driver", "top_constructor" ]) for cid, circuit_row in circuits.iterrows(): circuit_name = get_circuit_name(cid) location = circuit_row["location"] + ", " + circuit_row["country"] circuit_races = races[races["circuitId"] == cid] num_races = circuit_races.shape[0] years = circuit_races["year"].unique() years.sort() years = rounds_to_str(years) circuit_winners = results[(results["raceId"].isin(circuit_races.index)) & (results["position"] == 1)] driver_winners = circuit_winners["driverId"].value_counts() top_num_wins = driver_winners.iloc[0] top_driver_winners = [] for did, num_wins in driver_winners.iteritems(): if num_wins == top_num_wins: top_driver_winners.append( get_driver_name(did) + " (" + str(num_wins) + " wins)") else: break top_driver_winners = ", ".join(top_driver_winners) constructor_winners = circuit_winners["constructorId"].value_counts() top_num_wins = constructor_winners.iloc[0] top_constructor_winners = [] for constructor_id, num_wins in constructor_winners.iteritems(): if num_wins == top_num_wins: top_constructor_winners.append( get_constructor_name(constructor_id) + " (" + str(num_wins) + " wins)") else: break top_constructor_winners = ", ".join(top_constructor_winners) source = source.append( { "circuit_name": circuit_name, "location": location, "num_races": num_races, "years": years, "top_driver": top_driver_winners, "top_constructor": top_constructor_winners }, ignore_index=True) source = source.sort_values(by="num_races", ascending=False) circuits_columns = [ TableColumn(field="circuit_name", title="Circuit Name", width=150), TableColumn(field="location", title="Location", width=100), TableColumn(field="num_races", title="Num. Races", width=50), TableColumn(field="years", title="Years", width=130), TableColumn(field="top_driver", title="Top Winner(s) (Driver)", width=275), TableColumn(field="top_constructor", title="Top Winner(s) (Constructor)", width=200), ] circuits_table = DataTable(source=ColumnDataSource(data=source), columns=circuits_columns, index_position=None) title = Div(text="<h2><b>All Circuits</b></h2>") return column([title, circuits_table], sizing_mode="stretch_width")
def get_years(cid): years = races[races["circuitId"] == cid]["year"].unique() years.sort() return rounds_to_str(years)