Beispiel #1
0
def average_distance_over_weekday(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    weekdays_by_index = dict(zip(range(7), calendar.day_name))
    distances_by_index = dict(zip(range(7), [[] for x in range(7)]))
    for activity in rides:
        distances_by_index[activity.date.weekday()].append(activity.distance)

    average_distances = [statistics.mean(weekday_distances) for index, weekday_distances in distances_by_index.items()]

    adow_df = pd.DataFrame(data={
        "weekday": [weekdays_by_index[index] for index, distance in enumerate(average_distances)],
        "distances": average_distances
    })
    
    plt.clf()
    seaborn.set_theme()
    adow_plot = seaborn.barplot(x="weekday", y="distances", data=adow_df)
    adow_plot.set(xlabel="Day of Week", ylabel="Average Distance (miles)")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "adow.svg"))
    
    if arguments.show:
        plt.show()
Beispiel #2
0
def generate_aggregate_report(arguments):
    environment = Environment(loader=PackageLoader("cycloanalyzer",
                                                   "template"),
                              autoescape=select_autoescape(["html", "xml"]))
    environment.filters["format_number"] = format_number
    environment.filters["inject_class"] = inject_class
    template = environment.get_template("multi-report.html")

    rides = extract_activities(arguments.input,
                               imperial=True,
                               type_filter="Ride")

    first_datetime = rides[0].date
    last_datetime = rides[-1].date

    weekly_metrics = crunch.crunch_weekly_metrics(rides)
    ytd_metrics = crunch.crunch_year_to_date_metrics(rides)
    total_metrics = crunch.crunch_total_metrics(rides)

    arguments.show = False
    multi_plot.heatmap(arguments)
    multi_plot.average_distance_over_weekday(arguments)
    multi_plot.distance_over_time(arguments)
    multi_plot.distance_histogram(arguments)
    multi_plot.moving_time_histogram(arguments)

    heatmap_svg = remove_svg_dimensions(load_plot("heatmap.svg"))
    adow_svg = remove_svg_dimensions(load_plot("adow.svg"))
    dot_svg = remove_svg_dimensions(load_plot("dot.svg"))
    dhist_svg = remove_svg_dimensions(load_plot("dhist.svg"))
    thist_svg = remove_svg_dimensions(load_plot("thist.svg"))

    model = {
        "first_datetime": first_datetime,
        "last_datetime": last_datetime,
        "total_ride_count": total_metrics[0],
        "total_ride_time": total_metrics[1],
        "total_ride_distance": total_metrics[2],
        "total_ride_elevation": total_metrics[3],
        "ytd_ride_count": ytd_metrics[0],
        "ytd_ride_time": ytd_metrics[1],
        "ytd_ride_distance": ytd_metrics[2],
        "ytd_ride_elevation": ytd_metrics[3],
        "weekly_ride_average": weekly_metrics[0],
        "weekly_time_average": weekly_metrics[1],
        "weekly_distance_average": weekly_metrics[2],
        "weekly_elevation_average": weekly_metrics[3],
        "heatmap_svg": heatmap_svg,
        "adow_plot": adow_svg,
        "dot_plot": dot_svg,
        "dhist_plot": dhist_svg,
        "thist_plot": thist_svg
    }

    pathlib.Path("report").mkdir(exist_ok=True)
    with open(os.path.join("report", "multi-report.html"), "w") as report_file:
        report_file.write(template.render(model))
Beispiel #3
0
def heatmap(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    current_datetime = datetime.datetime.now()
    rides = [ride for ride in rides if ride.date.year == current_datetime.year]

    weekday_df = pd.DataFrame(data={
        "weekday": [ride.date.weekday() for ride in rides],
        "week_of_year": [ride.date.strftime("%U") for ride in rides],
        "distance": [ride.distance for ride in rides]
    })
    weekday_pivot = weekday_df.pivot(index="weekday", columns="week_of_year", values="distance")

    plt.clf()
    seaborn.set_theme()
    palette = seaborn.color_palette("crest", as_cmap=True)
    grid_kws = {"height_ratios": (.9, .05), "hspace": .05}
    figure, (ax, cbar_ax) = plt.subplots(2, gridspec_kw=grid_kws)
    ax = seaborn.heatmap(weekday_pivot,
        ax=ax,
        cbar_ax=cbar_ax,
        linewidths=1.0,
        cbar_kws={"orientation": "horizontal"},
        square=True,
        cmap=palette,
        xticklabels=1,
        yticklabels=1)
    ax.set(xlabel=None, ylabel=None)

    vertical_labels = ax.get_yticklabels()
    for label in vertical_labels:
        label.set_text(calendar.day_abbr[int(label.get_text())])
    ax.set_yticklabels(vertical_labels, rotation=0, horizontalalignment="right", fontsize="x-small")

    horizontal_labels = ax.get_xticklabels()
    last_label = None
    for label in horizontal_labels:
        week_of_year = int(label.get_text())
        rough_datetime = datetime.datetime.strptime("{}-{}-1".format(current_datetime.year, week_of_year), "%Y-%W-%w")
        rough_month = calendar.month_abbr[rough_datetime.month]
        if last_label is None or last_label != rough_month:
            label.set_text(rough_month)
        else:
            label.set_text(None)
        last_label = rough_month
    ax.set_xticklabels(horizontal_labels, rotation=45, fontsize="x-small")
    plt.title("Daily Distances, Year to Date")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "heatmap.svg"))

    if arguments.show:
        plt.show()
Beispiel #4
0
def generate_single_report(arguments):
    environment = Environment(loader=PackageLoader("cycloanalyzer",
                                                   "template"),
                              autoescape=select_autoescape(["html", "xml"]))
    environment.filters["format_number"] = format_number
    template = environment.get_template("single-report.html")

    activities = extract_activities(arguments.input,
                                    imperial=True,
                                    type_filter=None)
    selected_activity = crunch.select_activity(activities,
                                               iso_date=arguments.date)

    arguments.show = False
    single_plot.speed_over_time(arguments)
    single_plot.elevation_over_time(arguments)

    latlong_svg = None
    with open(os.path.join("plot", "latlong.svg"), "r") as latlong_file:
        latlong_svg = latlong_file.read()

    speed_svg = None
    with open(os.path.join("plot", "speed.svg"), "r") as speed_file:
        speed_svg = speed_file.read()

    elevation_svg = None
    with open(os.path.join("plot", "elevation.svg"), "r") as elevation_file:
        elevation_svg = elevation_file.read()

    model = {
        "name": selected_activity.name,
        "date": selected_activity.date,
        "top_speed": selected_activity.max_speed,
        "average_speed": selected_activity.average_speed,
        "elevation_gain": selected_activity.elevation_gain,
        "moving_time": selected_activity.moving_time / 60,
        "distance": selected_activity.distance,
        "average_grade": selected_activity.average_grade,
        "latlong_plot": latlong_svg,
        "speed_plot": speed_svg,
        "elevation_plot": elevation_svg
    }

    pathlib.Path("report").mkdir(exist_ok=True)
    with open(os.path.join("report", "single-report.html"),
              "w") as report_file:
        report_file.write(template.render(model))
Beispiel #5
0
def moving_time_histogram(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    time_df = pd.DataFrame(data={
        "moving_time": [ride.moving_time / 60 for ride in rides]
    })

    plt.clf()
    seaborn.set_theme()
    time_plot = seaborn.displot(time_df, x="moving_time", binwidth=15)
    time_plot.set(xlabel="Moving Time (minutes)", ylabel="Count")
    # plt.title("Distribution of Ride Times")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "thist.svg"))
    
    if arguments.show:
        plt.show()
Beispiel #6
0
def distance_histogram(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    distance_df = pd.DataFrame(data={
        "distance": [ride.distance for ride in rides]
    })
    
    plt.clf()
    seaborn.set_theme()
    distance_plot = seaborn.displot(distance_df, x="distance", binwidth=2)
    distance_plot.set(xlabel="Distance (miles)", ylabel="Count")
    # plt.title("Distribution of Ride Distances")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "dhist.svg"))
    
    if arguments.show:
        plt.show()
Beispiel #7
0
def average_speed_over_activities(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    asot_df = pd.DataFrame(data={
        "activity_date": [activity.date for activity in rides],
        "average_speed": [activity.average_speed if activity.average_speed else 0 for activity in rides]
    })

    plt.clf()
    seaborn.set_theme()
    asot_plot = seaborn.lineplot(x="activity_date", y="average_speed", data=asot_df)
    asot_plot.set(xlabel="Date", ylabel="Average Speed (mph)")
    plt.fill_between(asot_df.activity_date.values, asot_df.average_speed.values)

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "asot.svg"))
    
    if arguments.show:
        plt.show()
Beispiel #8
0
def speed_over_time(arguments):
    rides = extract_activities(arguments.input,
                               imperial=True,
                               type_filter="Ride")
    selected_activity = select_activity(rides, arguments.date)

    with open("export/" + selected_activity.filename, "r") as gpx_file:
        gpx = gpxpy.parse(gpx_file)

    trackpoints = []
    times_and_speeds = []
    for track in gpx.tracks:
        for segment in track.segments:
            for index, point in enumerate(segment.points):
                time = datetime.datetime.fromisoformat(point.time.isoformat())
                speed = 0
                if index < len(segment.points) - 1:
                    speed = point.speed_between(segment.points[index + 1])
                times_and_speeds.append((time, speed * 2.23694))

    speed_dataframe = pd.DataFrame(
        data={
            "datetime": [point[0] for point in times_and_speeds],
            "speed": [point[1] for point in times_and_speeds]
        })
    speed_dataframe["bin_speed"] = speed_dataframe.rolling(window=15).mean()

    plt.clf()
    seaborn.set_theme()
    avg_plot = seaborn.lineplot(x="datetime",
                                y="bin_speed",
                                data=speed_dataframe)
    avg_plot.set(xlabel="Time", ylabel="Speed (miles / hour)")
    plt.fill_between(speed_dataframe.datetime.values,
                     speed_dataframe.bin_speed.values)
    plt.title("Speed over Time")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "speed.svg"))

    if arguments.show:
        plt.show()
Beispiel #9
0
def latlong(arguments):
    """Plot an abstract plot of latitude/longitude scraped from the gpx data."""
    rides = extract_activities(arguments.input,
                               imperial=True,
                               type_filter="Ride")
    selected_activity = select_activity(rides, arguments.date)

    with open("export/" + selected_activity.filename, "r") as gpx_file:
        gpx = gpxpy.parse(gpx_file)

    trackpoints = []
    for track in gpx.tracks:
        for segment in track.segments:
            for index, point in enumerate(segment.points):
                time = datetime.datetime.fromisoformat(point.time.isoformat())
                trackpoints.append(
                    (time, point.latitude, point.longitude, point.elevation))

    latlong_dataframe = pd.DataFrame(
        data={
            "time": [point[0] for point in trackpoints],
            "latitude": [point[1] for point in trackpoints],
            "longitude": [point[2] for point in trackpoints],
            "elevation": [point[3] for point in trackpoints]
        })

    plt.clf()
    seaborn.set_theme(context="paper", style="white")
    seaborn.despine()
    latlong_plot = seaborn.lineplot(x="latitude",
                                    y="longitude",
                                    data=latlong_dataframe,
                                    sort=False,
                                    estimator=None,
                                    ci=None)
    latlong_plot.set(xlabel="", ylabel="")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "latlong.svg"))

    if arguments.show:
        plt.show()
Beispiel #10
0
def elevation_time_speed(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    ets_df = pd.DataFrame(data={
        "elevation": [float(activity.elevation_gain) for activity in rides],
        "moving_time": [float(activity.moving_time) / 60 for activity in rides],
        "average_speed": [float(activity.average_speed) * 2.237 if activity.average_speed else 0 for activity in rides]
    })

    plt.clf()
    seaborn.set_theme()
    ets_pivot = pd.pivot_table(ets_df, index="elevation", columns="moving_time", values="average_speed", aggfunc=np.average)
    f, ax = plt.subplots(figsize=(9, 6))
    ets_plot = seaborn.heatmap(ets_pivot, annot=True, linewidths=0.5, ax=ax)

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "ets.svg"))
    
    if arguments.show:
        plt.show()
Beispiel #11
0
def distance_over_time(arguments):
    """Do a basic scatterplot of distance over ride time."""
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    dot_by_id = {
        "distance": [ride.distance for ride in rides],
        "moving_time": [ride.moving_time / 60 for ride in rides],
        "average_speed": [ride.average_speed for ride in rides]
    }

    plt.clf()
    seaborn.set_theme()
    dot_df = pd.DataFrame(data=dot_by_id)
    dot_plot = seaborn.lmplot(x="moving_time", y="distance", data=dot_df)
    dot_plot.set(xlabel="Moving Time (Minutes)", ylabel="Distance (Miles)")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "dot.svg"))

    if arguments.show:
        plt.show()
Beispiel #12
0
def elevation_over_time(arguments):
    rides = extract_activities(arguments.input,
                               imperial=True,
                               type_filter="Ride")
    selected_activity = select_activity(rides, arguments.date)

    with open("export/" + selected_activity.filename, "r") as gpx_file:
        gpx = gpxpy.parse(gpx_file)

    trackpoints = []
    for track in gpx.tracks:
        for segment in track.segments:
            for point in segment.points:
                trackpoints.append(
                    (datetime.datetime.fromisoformat(point.time.isoformat()),
                     point.elevation))

    elevation_dataframe = pd.DataFrame(
        data={
            "datetime": [point[0] for point in trackpoints],
            "elevation": [point[1] for point in trackpoints]
        })

    plt.clf()
    seaborn.set_theme()
    elevation_plot = seaborn.lineplot(x="datetime",
                                      y="elevation",
                                      data=elevation_dataframe)
    elevation_plot.set(xlabel="Time", ylabel="Elevation (meters)")
    elevation_plot.axes.set_ylim(elevation_dataframe.elevation.min(),
                                 elevation_dataframe.elevation.max())
    plt.fill_between(elevation_dataframe.datetime.values,
                     elevation_dataframe.elevation.values)
    plt.title("Elevation over Time")

    pathlib.Path("plot").mkdir(exist_ok=True)
    plt.savefig(os.path.join("plot", "elevation.svg"))

    if arguments.show:
        plt.show()
Beispiel #13
0
def stats(arguments):
    rides = extract_activities(arguments.input, imperial=True, type_filter="Ride")

    current_datetime = datetime.datetime.now()
    first_datetime = rides[0].date
    last_datetime = rides[-1].date

    ride_names = [ride.name for ride in rides]
    ride_dates = [ride.date for ride in rides]
    ride_moving_times = [ride.moving_time for ride in rides]
    ride_distances = [ride.distance for ride in rides]
    date_df = pd.DataFrame(data={
        "name": ride_names,
        "date": ride_dates,
        "moving_time": ride_moving_times,
        "distance": ride_distances
    })
    rides_by_week = date_df.groupby(pd.Grouper(key="date", freq="W"))
    average_rides_per_week = round(statistics.mean([len(weekly_rides[1]) for weekly_rides in rides_by_week]), 2)
    average_time_per_week = round(statistics.mean([sum(weekly_rides[1]["moving_time"] / 60) for weekly_rides in rides_by_week]), 2)
    average_distance_per_week = round(statistics.mean([sum(weekly_rides[1]["distance"]) for weekly_rides in rides_by_week]), 2)
    
    min_distance = None
    min_elevation = None
    min_time = None
    max_distance = None
    max_elevation = None
    max_time = None
    total_distance = 0
    total_elevation = 0
    total_time = 0
    ytd_rides = 0
    ytd_distance = 0
    ytd_elevation = 0
    ytd_time = 0

    for ride in rides:
        if not min_distance or ride.distance < min_distance:
            min_distance = ride.distance
        if not max_distance or ride.distance > max_distance:
            max_distance = ride.distance
        if not min_elevation or ride.elevation_gain < min_elevation:
            min_elevation = ride.elevation_gain
        if not max_elevation or ride.elevation_gain > max_elevation:
            max_elevation = ride.elevation_gain
        if not min_time or ride.moving_time < min_time:
            min_time = ride.moving_time
        if not max_time or ride.moving_time > max_time:
            max_time = ride.moving_time
        total_distance += ride.distance
        total_elevation += ride.elevation_gain
        total_time += ride.moving_time
        if ride.date.year == current_datetime.year:
            ytd_rides += 1
            ytd_distance += ride.distance
            ytd_elevation += ride.elevation_gain
            ytd_time += ride.moving_time

    min_distance = round(min_distance, 2)
    max_distance = round(max_distance, 2)
    average_distance = round(total_distance / len(rides), 2)

    min_elevation = round(min_elevation, 2)
    max_elevation = round(max_elevation, 2)
    average_elevation = round(total_elevation / len(rides), 2)

    min_time_minutes = round(min_time / 60, 2)
    max_time_minutes = round(max_time / 60, 2)
    average_time_minutes = round(total_time / len(rides) / 60, 2)

    total_distance = round(total_distance, 2)
    total_elevation = round(total_elevation, 2)
    total_time_hours = round(total_time / 3600, 2)

    ytd_distance = round(ytd_distance, 2)
    ytd_elevation = round(ytd_elevation, 2)
    ytd_time_hours = round(ytd_time / 3600, 2)

    print(textwrap.dedent("""\
    ###########
    # Overall #
    ###########
    Average Rides Per Week: {}
    Average Time Per Week: {} minutes
    Average Distance Per Week: {} miles
    """.format(average_rides_per_week, average_time_per_week, average_distance_per_week)))

    print(textwrap.dedent("""\
    ################
    # Year To Date #
    ################
    Rides: {}
    Time: {} hours
    Distance: {} miles
    Elevation Gain: {} feet
    """.format(ytd_rides, ytd_time_hours, ytd_distance, ytd_elevation)))

    print(textwrap.dedent("""\
    ############
    # All Time #
    ############
    Rides: {}
    Time: {} hours
    Distance: {} miles
    Elevation Gain: {} feet
    """.format(len(rides), total_time_hours, total_distance, total_elevation)))

    print("Date Range: {} - {}".format(first_datetime.strftime("%b %d %Y"), last_datetime.strftime("%b %d %Y")))
    print("Distances: {} (min) {} (max) {} (avg) miles".format(min_distance, max_distance, average_distance))
    print("Elevation: {} (min) {} (max) {} (avg) feet".format(min_elevation, max_elevation, average_elevation))
    print("Moving Time: {} (min) {} (max) {} (avg) minutes".format(min_time_minutes, max_time_minutes, average_time_minutes))