forked from deepcharts/projects
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfast.py
More file actions
189 lines (146 loc) · 5.92 KB
/
fast.py
File metadata and controls
189 lines (146 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
## Deep Charts Youtube Channel: https://www.youtube.com/@DeepCharts
## Subscribe for more AI/Machine Learning/Data Science Tutorials
##################################
## 1. Data Import
##################################
import os
import markdown
import pandas as pd
from fasthtml.common import *
from fastcore.basics import NotStr
import plotly.express as px
import nfl_data_py as nfl
##################################
## 2. Initialize FastHTML app
##################################
app, rt = fast_app()
##################################
## 3. Input and Process Markdown Blog Files
##################################
# Directory containing Markdown files
POSTS_DIR = 'posts'
# Load and convert Markdown files to HTML
def load_posts():
posts = []
# List all Markdown files with their full paths
md_files = [os.path.join(POSTS_DIR, f) for f in os.listdir(POSTS_DIR) if f.endswith('.md')]
# Sort files by last modified time in descending order
md_files.sort(key=os.path.getmtime, reverse=True)
for filepath in md_files:
with open(filepath, 'r', encoding='utf-8') as file:
html_content = markdown.markdown(file.read())
title = os.path.basename(filepath).replace('_', ' ').replace('.md', '').title()
posts.append({"title": title, "content": html_content})
return posts
##################################
## 4. Function to import, wrangle, and graph data
##################################
# Generate NFL Cumulative Offensive Yards Chart
def generate_offensive_yards_chart():
# Fetch play-by-play data for the 2024 season
df = nfl.import_pbp_data([2024])
# Filter for rushing and passing plays
rushing_plays = df[df['play_type'] == 'run']
passing_plays = df[df['play_type'] == 'pass']
# Group by offensive team and week, then sum yards gained
weekly_rushing_yards = rushing_plays.groupby(['posteam', 'week'])['yards_gained'].sum().reset_index()
weekly_passing_yards = passing_plays.groupby(['posteam', 'week'])['yards_gained'].sum().reset_index()
# Add a 'play_type' column
weekly_rushing_yards['play_type'] = 'Rushing'
weekly_passing_yards['play_type'] = 'Passing'
# Combine the dataframes
combined_df = pd.concat([weekly_rushing_yards, weekly_passing_yards])
# Pivot the table to have teams as columns and weeks as rows
pivot_df = combined_df.pivot_table(index='week', columns=['posteam', 'play_type'], values='yards_gained', fill_value=0)
# Calculate cumulative yards
cumulative_yards = pivot_df.cumsum()
# Reset index for plotting
cumulative_yards = cumulative_yards.reset_index()
cumulative_yards.columns = ['week'] + [f'{team}_{ptype}' for team, ptype in cumulative_yards.columns[1:]]
# Melt the dataframe for Plotly Express
melted_df = cumulative_yards.melt(id_vars=['week'], var_name='team_playtype', value_name='cumulative_yards')
melted_df[['team', 'play_type']] = melted_df['team_playtype'].str.split('_', expand=True)
# Create Plotly Express figure
fig = px.line(melted_df, x='week', y='cumulative_yards', color='team', facet_col='play_type',
title='Cumulative Offensive Yards by Week (2024 Season)',
labels={'week': 'Week', 'cumulative_yards': 'Cumulative Yards'},
category_orders={'play_type': ['Rushing', 'Passing']})
fig.update_layout(legend_title_text='Team')
fig.update_xaxes(type='category')
return fig.to_html(full_html=False, include_plotlyjs='cdn')
##################################
## 5. Homepage Route for Content Layout
##################################
@rt('/')
def home():
posts = load_posts()
chart_html = generate_offensive_yards_chart()
# Create a list of article components for each post
article_posts = [
Article(
H1(post['title'], cls='post-title'),
Div(NotStr(post['content']))
)
for post in posts
]
return Html(
Head(
Title('Deep Charts: NFL Yards Tracker'),
Link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css'),
Style("""
.header {
text-align: center;
padding: 1em;
background-color: #f8f9fa;
position: fixed;
top: 0;
width: 100%;
z-index: 10;
}
.container {
display: flex;
max-width: 100%;
margin-top: 80px; /* Space for the fixed header */
}
.posts {
flex: 2;
overflow-y: auto;
height: calc(100vh - 80px); /* Adjust for header */
padding: 1em;
margin-right: 40%;
box-sizing: border-box;
}
.chart {
flex: 1;
position: fixed;
right: 0;
top: 80px; /* Space for the fixed header */
width: 40%;
height: calc(100vh - 80px); /* Adjust for header */
padding: 1em;
box-sizing: border-box;
}
h1.post-title {
font-size: 1.5em;
font-weight: bold;
}
article {
margin-bottom: 2em;
}
""")
),
Body(
Div(
H1('Deep Charts: NFL Yards Tracker', cls='header'),
Div(
Div(*article_posts, cls="posts"),
Div(NotStr(chart_html), cls="chart"),
cls="container"
)
)
)
)
##################################
## 6. Serve the App
##################################
serve()