HW1 - Interactive Visualizations
Databases and Plotly Visualization - Climate Data
For this tutorial we’re going to look into building SQLite databases from NOAA climate data and then wrangling that data to create interactive visualizations in Plotly.
First, lets import the requisite libraries.
import pandas as pd
import numpy as np
import sqlite3
Creating a Database
Next, we’ll read in our temperature data from the .csv (which should be stored in the same working directory as this notebook).
temps = pd.read_csv("temps_stacked.csv")
temps.head()
| ID | Year | Month | Temp | |
|---|---|---|---|---|
| 0 | ACW00011604 | 1961 | 1 | -0.89 |
| 1 | ACW00011604 | 1961 | 2 | 2.36 |
| 2 | ACW00011604 | 1961 | 3 | 4.72 |
| 3 | ACW00011604 | 1961 | 4 | 7.73 |
| 4 | ACW00011604 | 1961 | 5 | 11.28 |
…And then our country code csv (which we will use to interpret the weather station ID’s)
countries = pd.read_csv('countries.csv')
countries.head()
| FIPS 10-4 | ISO 3166 | Name | |
|---|---|---|---|
| 0 | AF | AF | Afghanistan |
| 1 | AX | - | Akrotiri |
| 2 | AL | AL | Albania |
| 3 | AG | DZ | Algeria |
| 4 | AQ | AS | American Samoa |
We then need to remove the whitespace in our column names so that everything play’s nice with SQL.
# whitespaces in column names are bad for SQL
countries = countries.rename(columns= {"FIPS 10-4": "FIPS_10-4", "ISO 3166": "ISO_3166"})
countries.head()
| FIPS_10-4 | ISO_3166 | Name | |
|---|---|---|---|
| 0 | AF | AF | Afghanistan |
| 1 | AX | - | Akrotiri |
| 2 | AL | AL | Albania |
| 3 | AG | DZ | Algeria |
| 4 | AQ | AS | American Samoa |
Lastly, we’ll read in our weather station geo-data from a url:
url = "https://raw.githubusercontent.com/PhilChodrow/PIC16B/master/datasets/noaa-ghcn/station-metadata.csv"
stations = pd.read_csv(url)
stations.head()
| ID | LATITUDE | LONGITUDE | STNELEV | NAME | |
|---|---|---|---|---|---|
| 0 | ACW00011604 | 57.7667 | 11.8667 | 18.0 | SAVE |
| 1 | AE000041196 | 25.3330 | 55.5170 | 34.0 | SHARJAH_INTER_AIRP |
| 2 | AEM00041184 | 25.6170 | 55.9330 | 31.0 | RAS_AL_KHAIMAH_INTE |
| 3 | AEM00041194 | 25.2550 | 55.3640 | 10.4 | DUBAI_INTL |
| 4 | AEM00041216 | 24.4300 | 54.4700 | 3.0 | ABU_DHABI_BATEEN_AIR |
Now we get into the sqlite. For each of our dataframes, we will invoke the .to_sql function which will read these df’s to our local sqlite mock database.
# open a connection to temps.db so that you can 'talk' to it using python
conn = sqlite3.connect("temps.db")
temps.to_sql("temperatures", conn, if_exists="replace", index=False)
countries.to_sql("countries", conn, if_exists="replace", index=False)
stations.to_sql("stations", conn, if_exists="replace", index=False)
# always close your connection
conn.close()
Querying our Database
Next we’ll write a function to query our data base, which accepts four arguments:
country: a string giving the name of a country (e.g. ‘South Korea’) for which data should be returned.
year_begin and year_end: two integers giving the earliest and latest years for which should be returned.
month: an integer giving the month of the year for which should be returned.
The return value of query_climate_database() is a Pandas dataframe of temperature readings for the specified country, in the specified date range, in the specified month of the year.
def query_climate_database(country, year_begin, year_end, month) :
"""
Function to return a Pandas dataframe of temperature readings for the
specified country, in the specified date range, in the specified month of the year,
by querying an sqlite database
Parameters
----------
country: str - the country to pull for
year_begin: str - start year for the query
year_end: str - end year for the query
month: str - month of the year to query for across specified year range
Returns
-------
df - pandas dataframe constructed by function
"""
conn = sqlite3.connect("temps.db") #open connection to temps database
#condition the sql command to include specified columns
cmd = \
"""
SELECT C.name, S.name, S.latitude, S.longitude, T.year, T.month, T.temp
FROM temperatures T
LEFT JOIN stations S ON T.id = S.id
LEFT JOIN countries C ON SUBSTRING(S.id, 1, 2) = C."FIPS_10-4"
WHERE C.name = (?) AND T.year >= (?) AND T.year <= (?) AND T.month = (?)
"""
#read data for our given command and connection to a pandas dataframe
df = pd.read_sql(cmd, conn, params=(country, year_begin, year_end, month))
conn.close() #close connection
return df #return our new dataframe
Now let’s test this function to make sure it works…
query_climate_database(country = "India", year_begin = 1980, year_end = 2020, month = 1)
| Name | NAME | LATITUDE | LONGITUDE | Year | Month | Temp | |
|---|---|---|---|---|---|---|---|
| 0 | India | PBO_ANANTAPUR | 14.583 | 77.633 | 1980 | 1 | 23.48 |
| 1 | India | PBO_ANANTAPUR | 14.583 | 77.633 | 1981 | 1 | 24.57 |
| 2 | India | PBO_ANANTAPUR | 14.583 | 77.633 | 1982 | 1 | 24.19 |
| 3 | India | PBO_ANANTAPUR | 14.583 | 77.633 | 1983 | 1 | 23.51 |
| 4 | India | PBO_ANANTAPUR | 14.583 | 77.633 | 1984 | 1 | 24.81 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 3147 | India | DARJEELING | 27.050 | 88.270 | 1983 | 1 | 5.10 |
| 3148 | India | DARJEELING | 27.050 | 88.270 | 1986 | 1 | 6.90 |
| 3149 | India | DARJEELING | 27.050 | 88.270 | 1994 | 1 | 8.10 |
| 3150 | India | DARJEELING | 27.050 | 88.270 | 1995 | 1 | 5.60 |
| 3151 | India | DARJEELING | 27.050 | 88.270 | 1997 | 1 | 5.70 |
3152 rows × 7 columns
Awesome, it works! Now let’s import plotly express to start working on our visualizations
import plotly.io as pio #only needed if saving figures
from plotly import express as px
Writing our First Geographic Scatter Function
Now, we’ll write a function to plot a visualization of the average yearly change in temperature for a given station. This function will accept five explicit arguments, and an undetermined number of keyword arguments.
country, year_begin, year_end, and month will be as in the previous part.
min_obs: the minimum required number of years of data for any given station. Only data for stations with at least min_obs years worth of data in the specified month should be plotted; the others should be filtered out.
kwargs: additional keyword arguments passed to px.scatter_mapbox(). These can be used to control the colormap used, the mapbox style, etc.
However, we’ll first need to define a function to estimate this yearly change. Let’s do so by computing the first coefficient of a linear regression for each station. Thus, it’s time to bring in our old faithful, the LR model from sklearn.
from sklearn.linear_model import LinearRegression #import LR model from sklearn
def coef(data_group):
"""
Function to return first order coefficient of model given a grouped df
Parameters
----------
data_group - the given grouped df to process
Returns
-------
slope - LR coefficient for year and temp of grouped, input df
"""
X = data_group[["Year"]]
y = data_group["Temp"]
LR = LinearRegression()
LR.fit(X, y)
slope = LR.coef_[0]
return slope
Now, we can move on to our plotting function. We’ll first invoke our prior query function to build our starting df. Then, we’ll need to group by name (grouping all enteries for a given station together) and dropping those which do not exceed our minimum observation parameter (as measured by calling .size on each grouping via transform). We’ll then apply our linear regression coefficient function to generate a new df, coef, which contains this new calculation, along with the names, latitudes, and longitudes for each entery. From here, we’ll then round our values and feed our original parameters to scatter_mapbox, including the kwargs, which allow the user to tweak all the various input arguments built into this plotly function.
def temperature_coefficient_plot(country, year_begin, year_end, month, min_obs, **kwargs):
"""
Function to create an interactive, geographic plot of estimated average increase
in temperature over a provided year range, for a given country, for a given month
Parameters
----------
country: str - the country to pull and plot for
year_begin: str - start year for the query
year_end: str - end year for the query
month: str - month of the year to query for across specified year range
min_obs: int - minimum number of observations for a station to be included
kwargs: variable arguments fed to the plotly graphing function
Returns
-------
fig - the plotly fig generated by the function
"""
#use prior defined query function to build our df
df = query_climate_database(country, year_begin, year_end, month)
#group by station name, find size of group (# of observations at station), add col
df["obs_count"] = df.groupby(["NAME"])["NAME"].transform(np.size)
#drop all entries where the total observations for that station < minimum observations
df = df.drop(df[df.obs_count < min_obs].index)
#group our df for a given country, in a given month, over a range of years by station
coefs = df.groupby(["NAME", "LATITUDE", "LONGITUDE"]).apply(coef)
coefs = coefs.reset_index()
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#pass dataframe and parameters to plot the scatter map_box
fig = px.scatter_mapbox(coefs, #our df w/ first order coeficients for yearly temp change
lat = "LATITUDE", #column name for latitude informataion
lon = "LONGITUDE", #column name for longitude information
hover_name = "NAME", #hover will display station name
**kwargs, #all other keyword args for scatter_mapbox()
color = "ESTIMATED YEAR INCREASE (C)",
color_continuous_midpoint=0)
#tweak layout and add title
str_month = (str(month))
str_start = (str(year_begin))
str_end = (str(year_end))
fig.update_layout(title = "Estimates of yearly increase in temperature in "
+ country + " years " + str_start + " - " + str_end)
return fig #return figure to calling scope
With the function written, let’s give it a go:
# assumes you have imported necessary packages
color_map = px.colors.diverging.RdGy_r # choose a colormap
fig = temperature_coefficient_plot("India", 1980, 2020, 1,
min_obs = 10,
zoom = 2,
mapbox_style="carto-positron",
color_continuous_scale=color_map)
fig.show()
pio.write_html(fig,"hw1_geo1") #comment out if not saving
Cool, look’s nice! Let’s try another country with another time window just for good measure:
#now for another country
color_map = px.colors.diverging.RdGy_r # choose a colormap
fig = temperature_coefficient_plot("Mexico", 1975, 2020, 1,
min_obs = 10,
zoom = 2,
mapbox_style="carto-positron",
color_continuous_scale=color_map)
fig.show()
pio.write_html(fig,"hw1_geo2") #comment out if not saving
So, let’s examine this idea starting with our new query function:
#function to query accross a latitude range
def lat_query(lat_start, lat_end):
"""
Function to return a Pandas dataframe of temperature readings for a specified
latitude range by querying an sqlite database
Parameters
----------
lat_start - int: the start of the latitude range to query for
lat_end - int: the end of the latitude range to query for
Returns
-------
df - pandas dataframe constructed by function
"""
conn = sqlite3.connect("temps.db") #open connection to temps database
#write command
cmd = \
"""
SELECT C.name, S.name, S.latitude, S.longitude, T.year, T.month, T.temp
FROM temperatures T
LEFT JOIN stations S ON T.id = S.id
LEFT JOIN countries C ON SUBSTRING(S.id, 1, 2) = C."FIPS_10-4"
WHERE S.latitude > (?) AND S.latitude < (?)
"""
#"""
#SELECT S.name, S.latitude, S.longitude, T.year, T.month, T.temp
#FROM temperatures T
#LEFT JOIN stations S ON T.id = S.id
#WHERE S.latitude > (?) AND S.latitude < (?)
#"""
#read data for our given command and connection to a pandas dataframe
df = pd.read_sql(cmd, conn, params=(lat_start, lat_end))
conn.close() #close connection
return df #return our new dataframe
df = lat_query(-30, 30)
df
| Name | NAME | LATITUDE | LONGITUDE | Year | Month | Temp | |
|---|---|---|---|---|---|---|---|
| 0 | United Arab Emirates | SHARJAH_INTER_AIRP | 25.333 | 55.517 | 1933 | 5 | 28.9 |
| 1 | United Arab Emirates | SHARJAH_INTER_AIRP | 25.333 | 55.517 | 1933 | 6 | 28.8 |
| 2 | United Arab Emirates | SHARJAH_INTER_AIRP | 25.333 | 55.517 | 1933 | 7 | 32.0 |
| 3 | United Arab Emirates | SHARJAH_INTER_AIRP | 25.333 | 55.517 | 1933 | 8 | 34.8 |
| 4 | United Arab Emirates | SHARJAH_INTER_AIRP | 25.333 | 55.517 | 1933 | 9 | 30.2 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 1983795 | Zimbabwe | GWELO | -19.430 | 29.750 | 1970 | 8 | 15.4 |
| 1983796 | Zimbabwe | GWELO | -19.430 | 29.750 | 1970 | 9 | 20.4 |
| 1983797 | Zimbabwe | GWELO | -19.430 | 29.750 | 1970 | 10 | 20.3 |
| 1983798 | Zimbabwe | GWELO | -19.430 | 29.750 | 1970 | 11 | 21.3 |
| 1983799 | Zimbabwe | GWELO | -19.430 | 29.750 | 1970 | 12 | 21.5 |
1983800 rows × 7 columns
Graphing Function 1.5 - Latitude Ranges Test
As a first test of the prior query, and the idea in general, let’s do a little remix of our prior geographic plot function, this time for a spatial, north/south range, rather than for a temporal one:
def temperature_coef_lat_plot(lat_start, lat_end, delta_threshold, **kwargs):
"""
Function to create an interactive, geographic plot of estimated average increase
in temperature over a provided latitude range
Parameters
----------
lat_start - int: the start of the latitude range to query for
lat_end - int: the end of the latitude range to query for
delta_threshold - int: threshold for inclusion in the plot (avoids say, every weather
station in America from being plotted)
kwargs: variable arguments fed to the plotly graphing function
Returns
-------
fig - the plotly fig generated by the function
"""
#use prior defined query function to build our df
df = lat_query(lat_start, lat_end)
#group our df by station
coefs = df.groupby(["NAME", "LATITUDE", "LONGITUDE"]).apply(coef)
coefs = coefs.reset_index()
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#drop all entries where the average yearly temp delta is below our threshold
coefs = coefs.drop(coefs["ESTIMATED YEAR INCREASE (C)"][coefs["ESTIMATED YEAR INCREASE (C)"] < delta_threshold].index)
#pass dataframe and parameters to plot the scatter map_box
fig = px.scatter_mapbox(coefs, #our df w/ first order coeficients for yearly temp change
lat = "LATITUDE", #column name for latitude informataion
lon = "LONGITUDE", #column name for longitude information
hover_name = "NAME", #hover will display station name
**kwargs, #all other keyword args for scatter_mapbox()
color = "ESTIMATED YEAR INCREASE (C)",
color_continuous_midpoint=0)
#tweak layout and add title
str_start = (str(lat_start))
str_end = (str(lat_end))
str_thres = (str(delta_threshold))
fig.update_layout(title = "Estimates of Yearly Increase in Temperature from "
+ str_start + " to " + str_end + " Degrees Latitude")
return fig #return figure to calling scope
#Note: I wrote one of the "coefs" above as "coef" and thus kept getting a "local
#variable referenced before assignment" for .apply(coef)...most baffling single letter
#typo turned bug I've had in a long time...it thought coefs the function was the local
#scope variable typo many lines down...I only rewrote half of it before I realized...
Now let’s test it:
fig = temperature_coef_lat_plot(60, 90, .2, zoom = 1,
mapbox_style="carto-positron",
color_continuous_scale=color_map)
fig.show()
pio.write_html(fig,"hw1_geo3")
Graphing Function 2 - Density Heatmap
Now that we’ve seen the concept in action, let’s try out an entirely different sort of plot, a density heatmap (2dhistogram). Now this is the real meat and potatoes of our idea. We’re going to write a function to create a plot that visually aggregates the average of the estimated average temperature change for a given latitude range. Here the bins (nxbins and nybins kwargs fed to the plotting function) determine the spatial resolution of our plot. The ratios of number of xbins to latitude range and number of ybins to longitude determine the x and y size of each bin, i.e. over how large of an area the stations are grouped together to average their yearly temp delta averages. Sounds weird right? It’ll make sense when you see it. It’s basically a flexible, Atari vibes, pixel-art-looking way to visualize temperate change that unlike a cloropleth, isn’t tied to geometry of borders or some other premade polygonal set; it’s visual aggregation you can change the resolution of and clumping behavior of using only a couple whole number inputs.
Here it goes:
def temperature_heat_map(lat_start, lat_end, delta_threshold, **kwargs):
"""
Function to create an interactive, geographic plot of estimated average increase
in temperature over a provided latitude range using a heat density map wherein
spatial resolution, and by proxy the grouping of temp delta averaging, via the
manipulation of bin counts and latitude ranges
Parameters
----------
lat_start - int: the start of the latitude range to query for
lat_end - int: the end of the latitude range to query for
delta_threshold - int: threshold for inclusion in the plot (avoids say, every weather
station in America from being plotted if you're looking for outliers only over a narrow
latitude band)
kwargs: variable arguments fed to the plotly graphing function
Returns
-------
fig - the plotly fig generated by the function
"""
#use prior defined query function to build our df
df = lat_query(lat_start, lat_end)
#group our df by station
coefs = df.groupby(["NAME", "LATITUDE", "LONGITUDE"]).apply(coef)
coefs = coefs.reset_index()
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#output strings
str_start = str(lat_start)
str_end = str(lat_end)
fig = px.density_heatmap(coefs, x="LONGITUDE", y="LATITUDE",
z="ESTIMATED YEAR INCREASE (C)", histfunc="avg",
color_continuous_midpoint=0,
title="Estimates of Yearly Change in Temperature from "
+ str_start + " to " + str_end + " Degrees Latitude",
**kwargs)
return fig
#call function
fig = temperature_heat_map(-90, 90, 0, nbinsx = 72, nbinsy = 36,
color_continuous_scale="rdbu_r")
#show figure
fig.show()
#comment out if you do not wish to save you fig
pio.write_html(fig,"hw4_heatdensity_map")
It makes a lot more sense once you see it, right? From this plot we can even make out actual landmasses…since that’s where the stations are…it’s basically a Minecraft map for ecological destruction. So what can we glean?
- The planet overall, is getting hotter on average, every year, for the later half of the 20th century as shown by the tint of the continents
- Polar latitudes are experiencing the highest average changes in both directions; this is where the most tumultuous temperature change rates are occuring compared to the more uniform, milder heating as one heads toward the equator.
- The Antartic in particular has seen the worst estimated average yearly increases in temperature.
- Could spatial shifts in artic air currents as the seas warm explain the spots of avg. temperature decrease seen in this area? I know the jet stream is certainly shifting, so things aren’t exactly stable in the North Atlantic.
So this map certainly indicates that the polar latitudes are experiencing the worst, absolute, average changes in temperature…if this was a four plot assignment I’d probably replot the above as relative to preindustrial baseline, calculation, but you get the idea.
Graphing Function 2.5 - Faceting
What would it look like if we broke the above plot down over a variable year range, into a variable number of subdivisions, and then calcuated the average change averages within those temporal subdivisions to better visual this change over time?
Enter the faceted density heatmap. Below we’ll write a function to plot these subdivisions of a given time frame all within the same plot, with consistent binning and scale between them:
def temperature_heat_map_faceted(lat_start, lat_end, start_year, end_year,
div_period, **kwargs):
"""
Function to create an interactive, geographic plot of estimated average increase
in temperature over a provided latitude range, for each of a specificied subdivision
duration of a specified year span using a faceted heat density map wherein
spatial resolution, and by proxy the grouping of temp delta averaging, via the
manipulation of bin counts and latitude ranges
Parameters
----------
lat_start - int: the start of the latitude range to query for
lat_end - int: the end of the latitude range to query for
start_year: str - start year for the query
start_year: str - end year for the query
div_period: int - length in years of each subdivision to plot
kwargs: variable arguments fed to the plotly graphing function
Returns
-------
fig - the plotly fig generated by the function
"""
#query using prior function
df = lat_query(lat_start, lat_end)
#year range filter
df = df[df["Year"] >= start_year]
df = df[df["Year"] <= end_year]
#loop through periods, moving year window
start = start_year
end = start_year + div_period
#build first period in column
period_string = str(start) + "-" + str(end)
df['Period'] = [period_string if (year >= start and year < end) else " " for year in df['Year']]
#update window
start = end
end = end + div_period
while(end <= end_year):
#logical index to fill periods
df.loc[((df["Year"] >= start) & (df["Year"] < end), 'Period')] = str(start) + "-" + str(end)
#update windows
start = end
end = end + div_period
#just in case, drop any unfilled vals
df = df[df["Period"] != " "]
#group our df for a given station,
coefs = df.groupby(["NAME", "LATITUDE", "LONGITUDE", "Period"]).apply(coef)
coefs = coefs.reset_index()
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#make output strings
str_start = str(start_year)
str_end = str(end_year)
str_div = str(div_period)
fig = px.density_heatmap(coefs, x="LONGITUDE", y="LATITUDE",
z="ESTIMATED YEAR INCREASE (C)", histfunc="avg",
color_continuous_midpoint=0, facet_row="Period",
title="Estimated Yearly Temp. Increase from " + str_start +
" to " + str_end + " by " + str_div + " Year Periods",
**kwargs)
#shorten color bar (it's quite long otherwise)
fig.update_layout(coloraxis_colorbar=dict(
len=0.5, ))
return fig
fig = temperature_heat_map_faceted(-90, 90, start_year=1980, end_year=2020, div_period=10,
width = 1000, height = 1500, nbinsx = 72, nbinsy = 36,
color_continuous_scale="rdbu_r")
fig.show()
pio.write_html(fig,"hw1_hd_faceted1") #comment out if not saving
Cool, so what does this faceted plot tell us?
- The 90’s were really rought for the Antarctic - we have +10 degree yearly delta estimates even!
- The more gradual yearly temp deltas as we approach the equator appear even more subtle when calculated within the decade, indicating that the overall delta estimates are likely skewed by particularly bad sub-periods—the pixel map of continents is replaced by more diffuse demarcations of more severe changes.
Graphing Function 3 - Histogram
So, this final function comes about mostly because the marginal histogram functionality for density heatmaps don’t play nice with averages instead of counts. So to answer the most literal and reduced version of what we’ve been asking, we’re going to make a plain-old histogram to show “What is the relationship between latitude band and estimated yearly temperature change?”
def lat_bar(lat_start, lat_end, start_year, end_year, **kwargs):
"""
Function to create an interactive, histogram of estimated average increase
in temperature over a provided latitude range, for a specified time span
in years.
Parameters
----------
lat_start - int: the start of the latitude range to query for
lat_end - int: the end of the latitude range to query for
start_year: str - start year for the query
start_year: str - end year for the query
kwargs: variable arguments fed to the plotly graphing function
Returns
-------
fig - the plotly fig generated by the function
"""
#use prior defined query function to build our df
df = lat_query(lat_start, lat_end)
#year range filter
df = df[df["Year"] >= start_year]
df = df[df["Year"] <= end_year]
#group our df by station
coefs = df.groupby(["NAME", "LATITUDE", "LONGITUDE"]).apply(coef)
coefs = coefs.reset_index()
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#rename new column
coefs = coefs.rename(columns= {0: "ESTIMATED YEAR INCREASE (C)"})
#round temp values
coefs = coefs.round(4)
#output strings
str_lat1 = str(lat_start)
str_lat2 = str(lat_end)
str_start = str(start_year)
str_end = str(end_year)
fig = px.histogram(coefs, x="LATITUDE", y="ESTIMATED YEAR INCREASE (C)",
nbins = (abs(lat_end - lat_start)//10), histfunc='avg',
title="Estimated Yearly Temp. Change by Latitude from " + str_start
+ " to " + str_end + " between " + str_lat1 + " and "
+ str_lat2 + '\u00b0')
return fig
fig = lat_bar(lat_start=-90, lat_end=0, start_year=1960, end_year=2010)
fig.show()
pio.write_html(fig,"hw1_hist1") #comment out if not saving
So we can see, somewhat more starkly than before, that from 1960 to 2010, looking at the south hemisphere to address the worst decades in terms of change for the highest rate of change area (the Antartic), that average yearly temperature changes get more severe the closer you get to the pole.
Thanks for reading; I hope you learned a bit about plotly.
