End interactive session 1C
Code
import pandas as pd
import matplotlib.pyplot as plt
url = "https://eds-217-essential-python.github.io/data/toolik_weather.csv"
df = pd.read_csv(url)π¬ The Whole Game, Part 1: Import to Aggregate
You are about to run a complete data science workflow. You will start with a raw file on the internet and finish with a monthly climate summary, all before you know how any single piece works. Today you run through all of it once. Over the next eight days you will learn how each part was built.
You are not expected to understand every line today. Your job is to copy code, run it, and change one thing at a time. Our job is to show you what a workflow looks like. By next Friday you will know how all of it works.
We will use real Arctic weather data from the Arctic LTER station at Toolik Lake, Alaska: daily measurements from 1988 onward. This session covers the first half of the workflow: Import, Explore, Group, and Aggregate. We skip Clean along the way, for a reason you will see. Session 1d finishes the job with Visualize and Export, and gives the whole workflow its name.
Set up your notebook with the usual ritual:
+ β Notebook β eds217_2026).Session_1C_Whole_Game_1.ipynb.# Day 1: Session 1C - The Whole Game, Part 1
[Session Webpage](https://eds-217-essential-python.github.io/course-materials/interactive-sessions/1c_whole_game_1.html)
Date: 08/31/2026Save often (Ctrl+S / Cmd+S).
You are going to run code you cannot fully read yet. To make that manageable, every step of the workflow follows the same small pattern. We chose this pattern for Day 1 to help you build confidence before you build understanding.
Each step has up to three parts:
df, so they leave your data alone. The next canonical cell brings everyone back together.The goal on Day 1 is to see the whole workflow and get a feel for each step. Mastering the syntax comes later. Running working code shows you where each step lands. Changing one thing gives you a safe way to experiment. Writing a sentence keeps you using the one skill you already own.
Import means loading your tools and then loading your data.
β Canonical cell. Copy and run it exactly:
The import ... as ... lines load Python toolkits under short nicknames. You will see three of them all course long: pd for pandas (data), plt for matplotlib (plots), and np for numpy (math). pd.read_csv(url) reads a CSV straight from the web into a DataFrame, a table, which we store in df.
Today read_csv() needs only one thing: the url of a CSV file. It can do much more, and you will learn that on Day 2.
read_csv builds a table, and you can give that table any name you like. Load the same data into a new variable, using one name from this menu:
climate = pd.read_csv(url)toolik = pd.read_csv(url)weather = pd.read_csv(url)Your original df stays exactly as it was. You have simply made a second copy under a new name.
Write an f-string that names what you loaded. For example, set dataset = "Toolik weather" and then print f"I loaded the {dataset} dataset."
Explore means getting to know the table before you analyze it.
β Canonical cell. Preview the first rows:
| Year | Month | Date | LTER_Site | Station | Daily_AirTemp_Mean_C | Flag_Daily_AirTemp_Mean_C | Daily_AirTemp_AbsMax_C | Flag_Daily_AirTemp_AbsMax_C | Daily_AirTemp_AbsMin_C | ... | Daily_Precip_Total_mm | Flag_Daily_Precip_Total_mm | Daily_windsp_mean_msec | FLAG_Daily_windsp_mean_msec | Daily_Windspeed_AbsMax_m_s | Daily_globalrad_total_jcm2 | FLAG_Daily_globalrad_total_mjm2 | Moss | Soil20cm | Comments | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1988 | 6 | 19880601 | ARC | TLKMAIN | 8.4 | E | NaN | NaN | NaN | ... | 0.0 | E | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Air temp 1 & 5 meter estimated from regressio... |
| 1 | 1988 | 6 | 19880602 | ARC | TLKMAIN | 6.0 | E | NaN | NaN | NaN | ... | 0.0 | E | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Air temp 1 & 5 meter estimated from regressio... |
| 2 | 1988 | 6 | 19880603 | ARC | TLKMAIN | 5.8 | E | NaN | NaN | NaN | ... | 0.0 | E | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Air temp 1 & 5 meter estimated from regressio... |
| 3 | 1988 | 6 | 19880604 | ARC | TLKMAIN | 1.8 | E | NaN | NaN | NaN | ... | 0.0 | E | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Air temp 1 & 5 meter estimated from regressio... |
| 4 | 1988 | 6 | 19880605 | ARC | TLKMAIN | 6.8 | E | NaN | NaN | NaN | ... | 2.5 | E | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Air temp 1 & 5 meter estimated from regressio... |
5 rows Γ 21 columns
Swap .head() for one other no-argument view of the same table:
df.info() shows the column names, types, and how many values are present.df.describe() shows summary statistics for the number columns.Each one looks at df without changing it.
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 11171 entries, 0 to 11170
Data columns (total 21 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Year 11171 non-null int64
1 Month 11171 non-null int64
2 Date 11171 non-null int64
3 LTER_Site 11171 non-null object
4 Station 11171 non-null object
5 Daily_AirTemp_Mean_C 11171 non-null float64
6 Flag_Daily_AirTemp_Mean_C 1310 non-null object
7 Daily_AirTemp_AbsMax_C 11001 non-null float64
8 Flag_Daily_AirTemp_AbsMax_C 1020 non-null object
9 Daily_AirTemp_AbsMin_C 10967 non-null float64
10 Flag_Daily_AirTemp_AbsMin_C 1236 non-null object
11 Daily_Precip_Total_mm 10751 non-null float64
12 Flag_Daily_Precip_Total_mm 3169 non-null object
13 Daily_windsp_mean_msec 10345 non-null float64
14 FLAG_Daily_windsp_mean_msec 1 non-null object
15 Daily_Windspeed_AbsMax_m_s 10325 non-null float64
16 Daily_globalrad_total_jcm2 4118 non-null float64
17 FLAG_Daily_globalrad_total_mjm2 15 non-null object
18 Moss 10333 non-null float64
19 Soil20cm 10351 non-null float64
20 Comments 8924 non-null object
dtypes: float64(9), int64(3), object(9)
memory usage: 1.8+ MB
| Year | Month | Date | Daily_AirTemp_Mean_C | Daily_AirTemp_AbsMax_C | Daily_AirTemp_AbsMin_C | Daily_Precip_Total_mm | Daily_windsp_mean_msec | Daily_Windspeed_AbsMax_m_s | Daily_globalrad_total_jcm2 | Moss | Soil20cm | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 11171.000000 | 11171.000000 | 1.117100e+04 | 11171.000000 | 11001.000000 | 10967.000000 | 10751.000000 | 10345.000000 | 10325.000000 | 4118.000000 | 10333.000000 | 10351.000000 |
| mean | 2003.203384 | 6.570674 | 2.003271e+07 | -8.196437 | -3.447132 | -13.295924 | 0.950953 | 2.995592 | 6.321288 | 1324.307188 | -1.471402 | -2.055647 |
| std | 8.831548 | 3.443637 | 8.830795e+04 | 14.831314 | 14.805298 | 14.727318 | 2.749060 | 1.553574 | 2.878432 | 866.670139 | 7.354649 | 5.555551 |
| min | 1988.000000 | 1.000000 | 1.988060e+07 | -55.600000 | -53.600000 | -57.600000 | 0.000000 | 0.000000 | 0.000000 | -2.000000 | -24.000000 | -21.000000 |
| 25% | 1996.000000 | 4.000000 | 1.996012e+07 | -19.700000 | -14.200000 | -25.600000 | 0.000000 | 2.000000 | 4.300000 | 591.000000 | -7.000000 | -6.000000 |
| 50% | 2003.000000 | 7.000000 | 2.003092e+07 | -7.100000 | -2.900000 | -12.300000 | 0.000000 | 2.800000 | 5.900000 | 1268.000000 | -2.000000 | -1.000000 |
| 75% | 2011.000000 | 10.000000 | 2.011051e+07 | 4.400000 | 8.900000 | -0.100000 | 0.400000 | 3.600000 | 7.800000 | 2026.000000 | 4.000000 | 2.000000 |
| max | 2018.000000 | 12.000000 | 2.018123e+07 | 20.700000 | 28.200000 | 14.900000 | 64.500000 | 12.500000 | 27.000000 | 3205.000000 | 19.000000 | 11.000000 |
Before you trust a dataset, run a quick health check. This is a fixed phrase you will reuse all course long. It counts the missing values in every column:
Year 0
Month 0
Date 0
LTER_Site 0
Station 0
Daily_AirTemp_Mean_C 0
Flag_Daily_AirTemp_Mean_C 9861
Daily_AirTemp_AbsMax_C 170
Flag_Daily_AirTemp_AbsMax_C 10151
Daily_AirTemp_AbsMin_C 204
Flag_Daily_AirTemp_AbsMin_C 9935
Daily_Precip_Total_mm 420
Flag_Daily_Precip_Total_mm 8002
Daily_windsp_mean_msec 826
FLAG_Daily_windsp_mean_msec 11170
Daily_Windspeed_AbsMax_m_s 846
Daily_globalrad_total_jcm2 7053
FLAG_Daily_globalrad_total_mjm2 11156
Moss 838
Soil20cm 820
Comments 2247
dtype: int64
Look at Daily_AirTemp_Mean_C. It has zero missing values, so the column we are about to analyze is complete. A few other columns do have gaps, which is normal. We only need our column to be clean.
df.info() reported how many rows the dataset has. Read that number, then report it in your own f-string. For example:
n_rows = 11171 # the number info() showed you
print(f"The Toolik dataset has {n_rows} daily records.")Now write a second sentence of your own, maybe about how many columns there are.
The next step in a normal workflow is Clean: fixing missing values, wrong types, and duplicate rows. Our health check already showed that Daily_AirTemp_Mean_C has no missing values, so there is nothing to clean for todayβs analysis, and we can go straight on.
Enjoy this while it lasts. Most real datasets need real cleaning, and it is often the biggest part of a project. You will spend all of Day 4 on it. Today the data came ready to use.
Group means splitting the rows into buckets by some key. Here we bucket every daily record by its Month, so all the Januaries sit together, all the Februaries, and so on.
β Canonical cell. Copy and run it exactly:
Change only the key you group by. Pick one from this menu:
by_year = df.groupby('Year') buckets records by year, which gives many groups.by_station = df.groupby('Station') gives a single bucket here, because every row is the same station.Both read df without changing it, and neither touches your monthly grouping.
Aggregate means collapsing each bucket down to a single number. Here we take the mean temperature within each month. Notice the two-step shape: first group, then pick a column and average it.
β Canonical cell. Copy and run it exactly:
Month
1 -22.889032
2 -20.700945
3 -20.692366
4 -11.762556
5 -0.795161
6 8.589892
7 11.222060
8 7.234860
9 -0.110753
10 -10.544225
11 -18.339355
12 -21.442560
Name: Daily_AirTemp_Mean_C, dtype: float64
That gives twelve numbers, one average temperature per month, summarizing thousands of daily records into a clear seasonal pattern.
Keep the grouping and change only the column you average. Pick one from this menu:
monthly['Daily_Precip_Total_mm'].mean() gives average daily precipitation.monthly['Daily_AirTemp_AbsMax_C'].mean() gives the average daily high temperature.Store the result under a new name so your monthly_means stays put.
Month
1 0.316590
2 0.490909
3 0.264937
4 0.317931
5 0.604227
6 1.537217
7 2.674922
8 2.041935
9 1.249828
10 0.671121
11 0.506000
12 0.425243
Name: Daily_Precip_Total_mm, dtype: float64
Look at your monthly_means, pick one monthβs value, read it off, and report it in an f-string. For example:
july_temp = 11.2 # July's value, read from monthly_means
print(f"At Toolik, the average July temperature is {july_temp} degrees Celsius.")Now write one more sentence for the month you find most surprising.
Now poke at it yourself. Try a different column, a different grouping key, or a deliberate typo. Errors are expected today, and they are welcome. A red error message is Python telling you something. See what you can make happen.
When the five minutes are up, run this cell to reload the data and rebuild the canonical results. Everyone returns to the same state, ready for Part 2:
pd.read_csv(url) loads a CSV from the web into a DataFrame. .head(), .info(), and .describe() are your no-argument ways to look at it.df.isnull().sum() is the health check for counting missing values.monthly = df.groupby('Month'), then monthly['Daily_AirTemp_Mean_C'].mean().df, so your data stays put and the group can always reconverge.β‘οΈ Next: In The Whole Game, Part 2 you will visualize these monthly means, save your results, and see the full ten-step workflow named.
End interactive session 1C