Esempio n. 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
Esempio n. 2
0
def generate_error_layout(circuit_id, constructor_id):
    """
    Generates an error layout in the event that the given constructor never competed at the given circuit.
    :param circuit_id: Circuit ID
    :param constructor_id: Constructor ID
    :return: Div layout
    """
    logging.info("Generating error layout")
    circuit_name = get_circuit_name(circuit_id, include_flag=False)
    constructor_name = get_constructor_name(constructor_id, include_flag=False)
    circuit_rids = races[races["circuitId"] == circuit_id].index.values
    constructors_at_that_circuit = results[results["raceId"].isin(
        circuit_rids)]["constructorId"].unique()
    rids_for_this_constructor = results[results["constructorId"] ==
                                        constructor_id]["raceId"]
    circuits_for_this_constructor = races[races.index.isin(
        rids_for_this_constructor)]["circuitId"].unique()

    # Generate the text
    text = f"Unfortunately, {constructor_name} never competed at {circuit_name}. Here are some other options:<br>"
    text += f"{constructor_name} competed at the following circuits..."
    text += "<ul>"
    for cid in circuits_for_this_constructor:
        text += f"<li>{get_circuit_name(cid)}</li>"
    text += "</ul>"
    text += f"The {circuit_name} hosted the following constructors..."
    text += "<ul>"
    for cid in constructors_at_that_circuit:
        text += f"<li>{get_constructor_name(cid)}</li>"
    text += "</ul><br>"

    layout = Div(text=text)
    return layout
def generate_error_layout(year_id, circuit_id, driver_id, constructor_id):
    logging.info("Generating error layout")
    driver_name = get_driver_name(driver_id)
    circuit_name = get_circuit_name(circuit_id)
    constructor_name = get_constructor_name(constructor_id)

    year_races = races[races["year"] == year_id]
    year_results = results[results["raceId"].isin(year_races.index)]
    constructors_this_driver = year_results[year_results["driverId"] == driver_id]["constructorId"].unique()
    circuit_years = sorted(races[races["circuitId"] == circuit_id]["year"].unique(), reverse=True)

    text = f"Unfortunately, {driver_name} did not compete at {circuit_name} in {year_id} for {constructor_name}.<br>"

    text += f"{driver_name} drove for the following constructors in {year_id}:<br>"
    text += "<ul>"
    for cid in constructors_this_driver:
        text += f"<li>{get_constructor_name(cid)}</li>"
    text += "</ul><br>"
    text += f"The following races happened in {year_id}:<br>"
    text += "<ul>"
    for rid in year_races.index.values:
        text += f"<li>{get_race_name(rid)}</li>"
    text += "</ul><br>"
    text += f"{circuit_name} hosted a Grand Prix in the following years:<br>"
    text += "<ul>"
    for year_id in circuit_years:
        text += f"<li>{year_id}</li>"
    text += "</ul><br>"
    layout = Div(text=text)
    return layout
Esempio n. 4
0
def get_layout(circuit_id=-1, download_image=True, **kwargs):
    circuit_races = races[races["circuitId"] == circuit_id]
    circuit_rids = circuit_races.index
    circuit_years = sorted(circuit_races["year"].values.tolist())
    circuit_fastest_lap_data = fastest_lap_data[fastest_lap_data["raceId"].isin(circuit_rids)]
    circuit_quali = qualifying[qualifying["raceId"].isin(circuit_rids)]
    circuit_results = results[results["raceId"].isin(circuit_rids)]
    circuit_driver_standings = driver_standings[driver_standings["raceId"].isin(circuit_rids)]

    logging.info(f"Generating layout for mode CIRCUIT in circuit, circuit_id={circuit_id}")

    times_plot = PlotItem(generate_times_plot, [circuit_years, circuit_quali, circuit_fastest_lap_data, circuit_races,
                                                circuit_results, circuit_id],
                          COMMON_PLOT_DESCRIPTIONS["generate_times_plot"])

    description = u"DNF Plot \u2014 plots number of DNFs and DNF percent vs year for this circuit"
    dnf_plot = PlotItem(generate_dnf_plot, [circuit_years, circuit_results, circuit_races, circuit_id], description)

    description = u"Average Start Position minus Finish Position Plot \u2014 " \
                  u"How many places do drivers make up on average?"
    spmfp_plot = PlotItem(generate_spmfp_plot, [circuit_years, circuit_races, circuit_results], description)

    spvfp_scatter = PlotItem(generate_spvfp_scatter, [circuit_results, circuit_races, circuit_driver_standings],
                             COMMON_PLOT_DESCRIPTIONS["generate_spvfp_scatter"])

    mltr_fp_scatter = PlotItem(generate_mltr_fp_scatter, [circuit_results, circuit_races, circuit_driver_standings],
                               COMMON_PLOT_DESCRIPTIONS["generate_mltr_fp_scatter"])

    description = u"Circuit Results Table \u2014 table of results for every GP held at this circuit"
    circuit_results_table = PlotItem(generate_circuit_results_table, [circuit_years, circuit_races, circuit_results,
                                                                      circuit_quali, circuit_fastest_lap_data],
                                     description)

    description = u"Various statistics on this circuit along with an image of the circuit layout"
    circuit_stats = PlotItem(generate_stats_layout, [circuit_id, circuit_years, circuit_fastest_lap_data,
                                                     circuit_results, circuit_races], description,
                             kwargs=dict(download_image=download_image))

    description = u"Winner's Table \u2014 table of the drivers and constructors who have won the most at this circuit"
    winners_table = PlotItem(generate_winners_table, [circuit_years, circuit_results, circuit_races], description)

    circuit_name = get_circuit_name(circuit_id)
    header = generate_div_item(f"<h2><b>{circuit_name}</b></h2><br>")

    middle_spacer = generate_spacer_item()
    group = generate_plot_list_selector([
        [header],
        [times_plot], [middle_spacer],
        [dnf_plot], [middle_spacer],
        [spmfp_plot], [middle_spacer],
        [spvfp_scatter, mltr_fp_scatter],
        [circuit_results_table],
        [circuit_stats],
        [winners_table]
    ])

    logging.info("Finished generating layout for mode CIRCUIT")

    return group
Esempio n. 5
0
def get_circuit_name_custom(cid):
    circuit_name = get_circuit_name(cid)
    flag = circuit_name[0:2]
    circuit_name = circuit_name[3:] + " " + flag
    if "Autódromo Internacional Nelson Piquet" in circuit_name:
        circuit_name = circuit_name.replace(
            "Autódromo Internacional Nelson Piquet",
            "Nelson Piquet (Jacarepaguá)")
    return circuit_name
Esempio n. 6
0
def generate_error_layout():
    """
    Generates an error layout in the event that the user selects an invalid circuit.
    :return: Error layout
    """
    text = "Somehow, you have selected an invalid circuit. The circuits we have data on are..."
    text += "<ul>"
    for cid in circuits.index:
        name = get_circuit_name(cid, include_flag=False)
        text += f"<li>{name}</li>"
    text += "</ul><br>"
    return Div(text=text)
def generate_error_layout(circuit_id, driver_id, constructor_id):
    """
    Generates error layout in the case where the given constructor with the given driver hasn't competed at the given
    circuit.
    :param circuit_id: Circuit ID
    :param driver_id: Driver ID
    :param constructor_id: Constructor ID
    :return: Error layout
    """
    logging.info("Generating error layout")
    circuit_name = get_circuit_name(circuit_id)
    driver_name = get_driver_name(driver_id)
    constructor_name = get_constructor_name(constructor_id)
    circuit_rids = races[races["circuitId"] == circuit_id].index.values
    circuit_results = results[results["raceId"].isin(circuit_rids)]
    # Constructors at this circuit
    constructors_at_circuit = circuit_results["constructorId"].unique()
    # Constructors of this driver at this circuit
    driver_constructors = circuit_results[circuit_results["driverId"] == driver_id]["constructorId"].unique()
    # Drivers for this constructor
    constructor_drivers = circuit_results[circuit_results["constructorId"] == constructor_id]["driverId"].unique()

    # Generate the text
    text = f"Unfortunately, {driver_name} never competed at {circuit_name} with {constructor_name}. " \
           f"Here are some other options:<br>"
    text += f"The following constructors competed at {circuit_name}..."
    text += "<ul>"
    for cid in constructors_at_circuit:
        text += f"<li>{get_constructor_name(cid)}</li>"
    text += "</ul><br>"
    text += f"{driver_name} raced at {circuit_name} while competing with the following constructors..."
    text += "<ul>"
    for cid in driver_constructors:
        text += f"<li>{get_constructor_name(cid)}</li>"
    text += "</ul><br>"
    text += f"{constructor_name} had the following drivers while competing at {circuit_name}..."
    text += "<ul>"
    for did in constructor_drivers:
        text += f"<li>{get_driver_name(did)}</li>"
    text += "</ul><br>"

    layout = Div(text=text)

    return layout
Esempio n. 8
0
def generate_times_plot(circuit_years, circuit_quali, circuit_fastest_lap_data, circuit_races, circuit_results,
                        circuit_id):
    """
    Plot quali, fastest lap, and average lap times vs year along with rating vs year
    :param circuit_years: Circuit years
    :param circuit_quali: Circuit quali
    :param circuit_fastest_lap_data: Circuit fastest lap data
    :param circuit_races: Circuit races
    :param circuit_results: Circuit results
    :param circuit_id: Circuit ID
    :return: Times plot layout
    """
    logging.info("Generating times plot")
    if circuit_quali.shape[0] == 0 and (circuit_fastest_lap_data.shape[0] == 0 or
                                        circuit_fastest_lap_data["avg_lap_time_millis"].isna().sum() ==
                                        circuit_fastest_lap_data["avg_lap_time_millis"].shape[0]):
        return Div(text="")
    source = pd.DataFrame(columns=["year",
                                   "quali_time", "quali_time_str",
                                   "fastest_lap_time", "fastest_lap_str",
                                   "avg_lap_time", "avg_lap_str",
                                   "rating"])

    for year in circuit_years:
        race = circuit_races[circuit_races["year"] == year]
        rid = race.index.values[0]

        # Qualifying
        year_quali = circuit_quali[circuit_quali["raceId"] == rid]
        quali_times = year_quali["q1"].append(year_quali["q2"].append(year_quali["q3"]))
        if quali_times.shape[0] == 0 or np.isnan(quali_times.idxmin()):
            quali_millis = np.nan
            quali_str = ""
        else:
            idxmin = int(quali_times.idxmin())
            quali_name = get_driver_name(year_quali.loc[idxmin, "driverId"])
            quali_millis = int(quali_times.loc[idxmin].min())
            quali_str = millis_to_str(quali_millis) + " by " + quali_name

        # Fastest and average lap
        year_fastest_lap_data = circuit_fastest_lap_data[circuit_fastest_lap_data["raceId"] == rid]
        if year_fastest_lap_data.shape[0] == 0:
            avg_lap_millis = np.nan
            avg_lap_str = ""
        else:
            avg_lap_millis = int(year_fastest_lap_data["avg_lap_time_millis"].mean())
            avg_lap_str = millis_to_str(avg_lap_millis)
        if year_fastest_lap_data.shape[0] == 0 or \
                year_fastest_lap_data["fastest_lap_time_millis"].isna().sum() == year_fastest_lap_data.shape[0]:
            fastest_lap_millis = np.nan
            fastest_lap_str = ""
        else:
            idxmin = int(year_fastest_lap_data["fastest_lap_time_millis"].idxmin())
            fastest_lap_name = get_driver_name(year_fastest_lap_data.loc[idxmin, "driver_id"])
            fastest_lap_millis = int(year_fastest_lap_data.loc[idxmin, "fastest_lap_time_millis"])
            fastest_lap_str = millis_to_str(fastest_lap_millis) + " by " + fastest_lap_name

        source = source.append({
            "year": year,
            "quali_time": quali_millis,
            "quali_time_str": quali_str,
            "fastest_lap_time": fastest_lap_millis,
            "fastest_lap_str": fastest_lap_str,
            "avg_lap_time": avg_lap_millis,
            "avg_lap_str": avg_lap_str,
            "rating": race["rating"].values[0],
        }, ignore_index=True)

    circuit_name = get_circuit_name(circuit_id)
    min_time = min(source["fastest_lap_time"].min(), source["avg_lap_time"].min(), source["quali_time"].min()) - 5000
    max_time = max(source["fastest_lap_time"].max(), source["avg_lap_time"].max(), source["quali_time"].max()) + 5000
    start = pd.to_datetime(min_time, unit="ms")
    end = pd.to_datetime(max_time, unit="ms")
    if pd.isna(start) or pd.isna(end):
        min_time = source["avg_lap_time"].min() - 5000
        max_time = source["avg_lap_time"].max() + 5000
        start = pd.to_datetime(min_time, unit="ms")
        end = pd.to_datetime(max_time, unit="ms")

    # Scale rating so that a 0=min_time, 10=max_time
    source["rating_scaled"] = (max_time - min_time) * (source["rating"] / 10) + min_time
    source["rating"] = source["rating"].fillna("")

    min_year = np.min(circuit_years)
    max_year = np.max(circuit_years)
    if min_year == max_year:
        min_year -= 0.01

    times_plot = figure(
        title=u"Qualifying, Fastest, Average, and Winning Times for " + circuit_name +
              " \u2014 Some data may be missing, zoom for more detail",
        x_axis_label="Year",
        y_axis_label="Lap Time",
        y_range=DataRange1d(start=start, end=end, bounds=(start, end)),
        x_range=Range1d(min_year, max_year, bounds=(min_year, max_year + 3)),
        tools="pan,xbox_zoom,xwheel_zoom,reset,box_zoom,wheel_zoom,save"
    )

    times_plot.yaxis.formatter = DatetimeTickFormatter(**DATETIME_TICK_KWARGS)
    column_source = ColumnDataSource(data=source)
    kwargs = {
        "x": "year",
        "source": column_source,
        "line_width": 2,
        "muted_alpha": 0.05
    }

    avg_lap_time_line = times_plot.line(y="avg_lap_time", line_color="white", **kwargs)
    legend_items = [
        LegendItem(label="Average Race Lap", renderers=[avg_lap_time_line]),
    ]
    tooltips = [
        ("Year", "@year"),
        ("Average Lap Time", "@avg_lap_str"),
    ]

    if source["quali_time"].isna().sum() < source.shape[0]:
        quali_time_line = times_plot.line(y="quali_time", line_color="red", **kwargs)
        legend_items.append(LegendItem(label="Qualifying Fastest", renderers=[quali_time_line]))
        tooltips.append(("Qualifying Lap Time", "@quali_time_str"))

    if source["fastest_lap_time"].isna().sum() < source.shape[0]:
        fastest_lap_time_line = times_plot.line(y="fastest_lap_time", line_color="yellow", **kwargs)
        legend_items.append(LegendItem(label="Fastest Race Lap", renderers=[fastest_lap_time_line]))
        tooltips.append(("Fastest Lap Time", "@fastest_lap_str"))

    # Add rating and other axis
    if source["rating"].replace("", np.nan).isna().sum() < source.shape[0]:
        rating_line = times_plot.line(y="rating_scaled", line_color="green", line_alpha=0.9, name="rating_line",
                                      **kwargs)
        legend_items.append(LegendItem(label="Average Rating", renderers=[rating_line]))
        tooltips.append(("Rating", "@rating"))
        y_range = Range1d(start=0, end=10, bounds=(0, 10))
        times_plot.extra_y_ranges = {"rating_range": y_range}
        axis = LinearAxis(y_range_name="rating_range", axis_label="Rating")
        times_plot.add_layout(axis, "right")

        def update_rating_axis():
            def dt_to_millis(t):
                if isinstance(t, float) or isinstance(t, int):
                    return t
                return t.microsecond / 1000 + t.second * 1000 + t.minute * 1000 * 60
            max_time = dt_to_millis(times_plot.y_range.end)
            min_time = dt_to_millis(times_plot.y_range.start)
            new_rating = (max_time - min_time) * (source["rating"].replace("", np.nan).astype(float) / 10) + min_time
            column_source.patch({
                "rating_scaled": [(slice(new_rating.shape[0]), new_rating)]
            })
            times_plot.extra_y_ranges.update({"rating_range": Range1d(start=0, end=10, bounds=(0, 10))})

        times_plot.y_range.on_change("start", lambda attr, old, new: update_rating_axis())
        times_plot.y_range.on_change("end", lambda attr, old, new: update_rating_axis())

    # Legend
    legend = Legend(items=legend_items, location="top_right", glyph_height=15, spacing=2, inactive_fill_color="gray")
    times_plot.add_layout(legend, "right")
    times_plot.legend.click_policy = "mute"
    times_plot.legend.label_text_font_size = "12pt"  # The default font size

    # Hover tooltip
    times_plot.add_tools(HoverTool(show_arrow=False, tooltips=tooltips))

    # Crosshair
    times_plot.add_tools(CrosshairTool(line_color="white", line_alpha=0.6))

    return times_plot
Esempio n. 9
0
def generate_dnf_plot(circuit_years, circuit_results, circuit_races, circuit_id):
    """
    Plots number of races, number of DNFs, and DNF percent for that year on the same plot. (2 different axes).
    :param circuit_years: Circuit years
    :param circuit_results: Circuit results
    :param circuit_races: Circuit races
    :param circuit_id: Circuit ID
    :return: Plot layout
    """
    # TODO refactor to use existing method
    logging.info("Generating dnf plot")
    if len(circuit_years) == 0:
        return Div()
    source = pd.DataFrame(columns=["n_races", "year", "n_drivers",
                                   "dnf_pct", "dnfs", "dnf_pct_str",
                                   "total_dnf_pct", "total_dnfs", "total_dnf_pct_str"])
    n_races = 0
    total_dnfs = 0
    total_drivers = 0
    for year in circuit_years:
        year_race = circuit_races[circuit_races["year"] == year]
        if year_race.shape[0] == 0:
            continue
        rid = year_race.index.values[0]
        year_results = circuit_results[circuit_results["raceId"] == rid]
        num_dnfs = year_results["position"].isna().sum()
        num_drivers = year_results.shape[0]
        total_dnfs += num_dnfs
        total_drivers += num_drivers
        if num_drivers > 0:
            dnf_pct = num_dnfs / num_drivers
            total_dnf_pct = total_dnfs / total_drivers
            n_races += 1
            source = source.append({
                "n_races": n_races,
                "n_drivers": num_drivers,
                "year": year,
                "dnf_pct": dnf_pct,
                "dnfs": num_dnfs,
                "dnf_pct_str": str(round(100 * dnf_pct, 1)) + "%",
                "total_dnf_pct": total_dnf_pct,
                "total_dnfs": total_dnfs,
                "total_dnf_pct_str": str(round(100 * total_dnf_pct, 1)) + "%",
            }, ignore_index=True)

    circuit_name = get_circuit_name(circuit_id)
    min_year = min(circuit_years)
    max_year = max(circuit_years)
    max_drivers = source["n_drivers"].max()
    if max_drivers == 0:
        return Div()
    dnf_plot = figure(
        title=u"Number of DNFs \u2014 " + circuit_name,
        y_axis_label="",
        x_axis_label="Year",
        x_range=Range1d(min_year, max_year, bounds=(min_year, max_year + 3)),
        tools="pan,xbox_zoom,xwheel_zoom,reset,box_zoom,wheel_zoom,save",
        y_range=Range1d(0, max_drivers, bounds=(-1000, 1000))
    )

    subtitle = 'Year DNFs refers to the number/percent of DNFs for that year, Total DNFs refers to all DNFs up to ' \
               'that point in time'
    dnf_plot.add_layout(Title(text=subtitle, text_font_style="italic"), "above")

    max_dnf_pct = max(source["dnf_pct"].max(), source["total_dnf_pct"].max())
    if max_dnf_pct > 0:
        k = max_drivers / max_dnf_pct
    else:
        k = 1
    source["dnf_pct_scaled"] = k * source["dnf_pct"]
    source["total_dnf_pct_scaled"] = k * source["total_dnf_pct"]

    # Other y axis
    y_range = Range1d(start=0, end=max_dnf_pct, bounds=(-0.02, 1000))
    dnf_plot.extra_y_ranges = {"percent_range": y_range}
    axis = LinearAxis(y_range_name="percent_range")
    axis.formatter = NumeralTickFormatter(format="0.0%")
    dnf_plot.add_layout(axis, "right")

    kwargs = {
        "x": "year",
        "line_width": 2,
        "line_alpha": 0.7,
        "source": source,
        "muted_alpha": 0.05
    }

    races_line = dnf_plot.line(y="n_races", color="white", **kwargs)
    drivers_line = dnf_plot.line(y="n_drivers", color="yellow", **kwargs)
    dnfs_line = dnf_plot.line(y="dnfs", color="aqua", **kwargs)
    dnf_pct_line = dnf_plot.line(y="dnf_pct_scaled", color="aqua", line_dash="dashed", **kwargs)
    total_dnfs_line = dnf_plot.line(y="total_dnfs", color="orange", **kwargs)
    total_dnf_pct_line = dnf_plot.line(y="total_dnf_pct_scaled", color="orange", line_dash="dashed", **kwargs)

    legend = [LegendItem(label="Number of Races", renderers=[races_line]),
              LegendItem(label="Number of Drivers", renderers=[drivers_line]),
              LegendItem(label="Year DNFs", renderers=[dnfs_line]),
              LegendItem(label="Year DNF Pct.", renderers=[dnf_pct_line]),
              LegendItem(label="Total DNFs", renderers=[total_dnfs_line]),
              LegendItem(label="Total DNF Pct.", renderers=[total_dnf_pct_line])]

    legend = Legend(items=legend, location="top_right", glyph_height=15, spacing=2, inactive_fill_color="gray")
    dnf_plot.add_layout(legend, "right")
    dnf_plot.legend.click_policy = "mute"
    dnf_plot.legend.label_text_font_size = "12pt"

    # Hover tooltip
    dnf_plot.add_tools(HoverTool(show_arrow=False, tooltips=[
        ("Number of Races", "@n_races"),
        ("Number of Drivers", "@n_drivers"),
        ("Year Num. DNFs", "@dnfs (@dnf_pct_str)"),
        ("Total Num. DNFs", "@total_dnfs (@total_dnf_pct_str)"),
        ("Year", "@year"),
    ]))

    # Crosshair tooltip
    dnf_plot.add_tools(CrosshairTool(line_color="white", line_alpha=0.6))

    return dnf_plot
Esempio n. 10
0
def get_layout(circuit_id=-1,
               constructor_id=-1,
               download_image=True,
               **kwargs):
    # Grab some useful slices
    circuit_rids = races[races["circuitId"] == circuit_id].index.values
    constructor_results = results[results["constructorId"] == constructor_id]
    cc_results = constructor_results[constructor_results["raceId"].isin(
        circuit_rids)]
    cc_rids = cc_results["raceId"]
    cc_races = races[races.index.isin(cc_rids)]

    logging.info(
        f"Generating layout for mode CIRCUITCONSTRUCTOR in circuitconstructor, circuit_id={circuit_id}, "
        f"constructor_id={constructor_id}")

    # Detect invalid combos
    if cc_races.shape[0] == 0:
        return generate_error_layout(circuit_id, constructor_id)

    # Generate some more slices
    circuit_results = results[results["raceId"].isin(circuit_rids)]
    circuit_fastest_lap_data = fastest_lap_data[
        fastest_lap_data["raceId"].isin(circuit_rids)]
    cc_years = cc_races["year"].unique()
    cc_years.sort()
    cc_fastest_lap_data_idxs = []
    cc_lap_time_idxs = []
    cc_driver_standings_idxs = []
    for idx, results_row in cc_results.iterrows():
        rid = results_row["raceId"]
        did = results_row["driverId"]
        fl_slice = circuit_fastest_lap_data[
            circuit_fastest_lap_data["driver_id"] == did]
        cc_fastest_lap_data_idxs.extend(fl_slice.index.values.tolist())
        lt_slice = lap_times[(lap_times["raceId"] == rid)
                             & (lap_times["driverId"] == did)]
        cc_lap_time_idxs.extend(lt_slice.index.values.tolist())
        driver_standings_slice = driver_standings[
            (driver_standings["raceId"] == rid)
            & (driver_standings["driverId"] == did)]
        cc_driver_standings_idxs.extend(
            driver_standings_slice.index.values.tolist())
    cc_fastest_lap_data = circuit_fastest_lap_data.loc[
        cc_fastest_lap_data_idxs]
    cc_lap_times = lap_times.loc[cc_lap_time_idxs]
    cc_driver_standings = driver_standings.loc[cc_driver_standings_idxs]
    constructor_constructor_standings = constructor_standings[
        constructor_standings["constructorId"] == constructor_id]

    positions_plot, positions_source = generate_positions_plot(
        cc_years, cc_fastest_lap_data, constructor_results,
        constructor_constructor_standings, cc_races, constructor_id)
    positions_plot = PlotItem(
        positions_plot, [],
        COMMON_PLOT_DESCRIPTIONS["generate_positions_plot"])

    win_plot = PlotItem(generate_win_plot, [positions_source, constructor_id],
                        COMMON_PLOT_DESCRIPTIONS["generate_win_plot"])

    lap_time_distribution_plot = PlotItem(
        generate_lap_time_plot,
        [cc_lap_times, cc_rids, circuit_id, constructor_id],
        COMMON_PLOT_DESCRIPTIONS["generate_times_plot"])

    finish_position_bar_plot = PlotItem(
        generate_finishing_position_bar_plot, [cc_results],
        COMMON_PLOT_DESCRIPTIONS["generate_finishing_position_bar_plot"])

    spvfp_scatter = PlotItem(
        generate_spvfp_scatter, [cc_results, cc_races, cc_driver_standings],
        COMMON_PLOT_DESCRIPTIONS["generate_spvfp_scatter"])

    mltr_fp_scatter = PlotItem(
        generate_mltr_fp_scatter, [cc_results, cc_races, cc_driver_standings],
        COMMON_PLOT_DESCRIPTIONS["generate_mltr_fp_scatter"])

    description = u"Various statistics on this constructor at this circuit"
    stats_div = PlotItem(generate_stats_layout, [
        cc_years, cc_races, cc_results, cc_fastest_lap_data, positions_source,
        circuit_id, constructor_id
    ], description)

    description = u"Results Table \u2014 table showing this constructor's results at every race this circuit"
    results_table = PlotItem(generate_results_table, [
        cc_results, cc_fastest_lap_data, circuit_results,
        circuit_fastest_lap_data
    ], description)

    if download_image:
        # Track image
        circuit_row = circuits.loc[circuit_id]
        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()
    image_view = PlotItem(image_view, [], "", listed=False)

    circuit_name = get_circuit_name(circuit_id)
    constructor_name = get_constructor_name(constructor_id)
    header = generate_div_item(
        f"<h2><b>What did/does {constructor_name}'s performance at "
        f"{circuit_name} look like?</b></h2><br>")

    middle_spacer = generate_spacer_item()
    group = generate_plot_list_selector([[header], [positions_plot],
                                         [middle_spacer], [win_plot],
                                         [middle_spacer],
                                         [lap_time_distribution_plot],
                                         [middle_spacer],
                                         [finish_position_bar_plot],
                                         [middle_spacer],
                                         [spvfp_scatter, mltr_fp_scatter],
                                         [middle_spacer], [image_view],
                                         [stats_div], [results_table]])

    logging.info("Finished generating layout for mode CIRCUITCONSTRUCTOR")

    return group
Esempio n. 11
0
def generate_lap_time_plot(cd_lap_times,
                           cd_rids,
                           circuit_id,
                           driver_id,
                           constructor_id=None):
    """
    Plot lap time distribution of the driver at this circuit along with the lap time distribution of all drivers at this
    circuit during the time period to show how fast and consistent he is.
    :param cd_lap_times: Circuit driver lap times
    :param cd_rids: Circuit driver race IDs
    :param circuit_id: Circuit ID
    :param driver_id: Driver ID, can be set to None if using constructor mode
    :param constructor_id: Constructor ID, set to None if not using constructor mode
    :return: Lap time plot layout
    """
    logging.info("Generating lap time distribution plot")
    # Collect data on everyone during these years
    all_times = lap_times[lap_times["raceId"].isin(cd_rids)]
    millis_range_min = all_times["milliseconds"].mean(
    ) - 1 * all_times["milliseconds"].std()
    millis_range_max = all_times["milliseconds"].mean(
    ) + 2 * all_times["milliseconds"].std()

    cd_lap_times = cd_lap_times[
        (cd_lap_times["milliseconds"] > millis_range_min)
        & (cd_lap_times["milliseconds"] < millis_range_max)]
    if cd_lap_times.shape[0] == 0:
        return Div(
            text=
            "Unfortunately, we do not yet have lap time data on this driver at this circuit."
        )
    all_times = all_times[(all_times["milliseconds"] > millis_range_min)
                          & (all_times["milliseconds"] < millis_range_max)]

    cd_hist, cd_edges = np.histogram(cd_lap_times["milliseconds"], bins=50)
    all_hist, all_edges = np.histogram(all_times["milliseconds"], bins=50)
    cd_hist = cd_hist / cd_lap_times.shape[0]
    all_hist = all_hist / all_times.shape[0]

    all_pdf_source = pd.DataFrame(columns=["x", "pdf"])
    cd_pdf_source = pd.DataFrame(columns=["x", "pdf"])
    for i in range(0, all_edges.shape[0] - 1):
        x = 0.5 * (all_edges[i] + all_edges[i + 1])
        pdf = all_hist[i]
        all_pdf_source = all_pdf_source.append({
            "x": x,
            "pdf": pdf
        },
                                               ignore_index=True)
    for i in range(0, cd_edges.shape[0] - 1):
        x = 0.5 * (cd_edges[i] + cd_edges[i + 1])
        pdf = cd_hist[i]
        cd_pdf_source = cd_pdf_source.append({
            "x": x,
            "pdf": pdf
        },
                                             ignore_index=True)
    all_pdf_source["lap_time_str"] = all_pdf_source["x"].apply(millis_to_str)
    cd_pdf_source["lap_time_str"] = all_pdf_source["x"].apply(millis_to_str)
    all_pdf_source["pct_str"] = all_pdf_source["pdf"].apply(
        lambda pdf: str(round(100 * pdf, 1)) + "%")
    cd_pdf_source["pct_str"] = cd_pdf_source["pdf"].apply(
        lambda pdf: str(round(100 * pdf, 1)) + "%")

    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)
    circuit_name = get_circuit_name(circuit_id, include_flag=False)
    title = u"Lap Time Distribution \u2014 " + name + "'s lap times at " + circuit_name + \
            " vs the rest of the field during their years"
    max_y = 0.02 + max(cd_pdf_source["pdf"].max(), all_pdf_source["pdf"].max())
    min_x = all_edges[0] - 500
    max_x = all_edges[-1] + 500
    time_dist = figure(title=title,
                       y_axis_label="% Occurrence",
                       x_axis_label="Lap Time",
                       y_range=Range1d(0, max_y, bounds=(0, max_y)),
                       x_range=Range1d(min_x, max_x,
                                       bounds=(min_x, max_x + 3)),
                       tools="pan,box_zoom,wheel_zoom,reset,save")
    subtitle = "Only lap times within 2 standard deviations of the mean are shown, means marked with horizontal line"
    time_dist.add_layout(Title(text=subtitle, text_font_style="italic"),
                         "above")
    time_dist.xaxis.formatter = DatetimeTickFormatter(**DATETIME_TICK_KWARGS)
    time_dist.yaxis.formatter = NumeralTickFormatter(format="0.0%")

    cd_quad = time_dist.quad(top=cd_hist,
                             bottom=0,
                             left=cd_edges[:-1],
                             right=cd_edges[1:],
                             fill_color="orange",
                             line_alpha=0,
                             alpha=0.1,
                             muted_alpha=0)

    line_kwargs = dict(x="x",
                       y="pdf",
                       line_alpha=0.9,
                       line_width=2,
                       muted_line_alpha=0.05)
    cd_pdf_line = time_dist.line(source=cd_pdf_source,
                                 color="orange",
                                 **line_kwargs)
    all_pdf_line = time_dist.line(source=all_pdf_source,
                                  color="white",
                                  **line_kwargs)

    # Mark means
    line_kwargs = dict(y=[-100, 100],
                       line_alpha=0.9,
                       line_width=2,
                       muted_alpha=0.05)
    all_mean = all_times["milliseconds"].mean()
    cd_mean = cd_lap_times["milliseconds"].mean()
    cd_mean_line = time_dist.line(x=[cd_mean] * 2,
                                  line_color="orange",
                                  **line_kwargs)
    all_mean_line = time_dist.line(x=[all_mean] * 2,
                                   line_color="white",
                                   **line_kwargs)

    # Legend
    legend = [
        LegendItem(label=f"{name}'s Dist.",
                   renderers=[cd_pdf_line, cd_quad, cd_mean_line]),
        LegendItem(label="All Drivers Dist.",
                   renderers=[all_pdf_line, all_mean_line])
    ]

    legend = Legend(items=legend,
                    location="top_right",
                    glyph_height=15,
                    spacing=2,
                    inactive_fill_color="gray")
    time_dist.add_layout(legend, "right")
    time_dist.legend.click_policy = "mute"
    time_dist.legend.label_text_font_size = "12pt"

    # Hover tooltip
    time_dist.add_tools(
        HoverTool(show_arrow=False,
                  renderers=[all_pdf_line, cd_pdf_line],
                  tooltips=[("Lap Time", "@lap_time_str"),
                            ("Percent of Laps", "@pct_str")]))

    # Crosshair tooltip
    time_dist.add_tools(CrosshairTool(line_color="white", line_alpha=0.6))

    return time_dist
Esempio n. 12
0
# Circuit stats
generate_stats_layout = time_decorator(generate_stats_layout)
circuit_stats = generate_stats_layout(circuit_id, circuit_years, circuit_fastest_lap_data, circuit_results,
                                      circuit_races)

# Winner's table
generate_winners_table = time_decorator(generate_winners_table)
winners_table = generate_winners_table(circuit_years, circuit_results, circuit_races)

n = circuits.index.unique().shape[0]
i = 1

for circuit_id in circuits.index.unique():
    try:
        name = get_circuit_name(circuit_id, include_flag=False)
        print(f"Testing circuit ID {circuit_id}, {name}, {i} / {n}")
        i += 1
        start = time.time()
        get_layout(circuit_id=circuit_id, download_image=False)
        end = time.time()
        elapsed = 1000 * (end - start)
        print(f"Completed in  {elapsed} milliseconds")
        times["cid"].append(circuit_id)
        times["time"].append(elapsed)
    except Exception as e:
        print(f"Encountered exception: {e}")
        track = traceback.format_exc()
        print("The traceback is:")
        print(track)
        error_ids.append(circuit_id)
Esempio n. 13
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")