Example #1
0
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
Example #2
0
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")
Example #3
0
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")
Example #4
0
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)
Example #5
0
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")
Example #6
0
 def get_years(cid):
     years = races[races["circuitId"] == cid]["year"].unique()
     years.sort()
     return rounds_to_str(years)