Skip to content

Commit cb43fef

Browse files
poetry applications
1 parent 7d07a97 commit cb43fef

29 files changed

+1200
-43
lines changed

figures/poetryplots/.DS_Store

6 KB
Binary file not shown.

figures/poetryplots/df-plot.pdf

14.4 KB
Binary file not shown.

figures/poetryplots/heat-basic.pdf

14 KB
Binary file not shown.

figures/poetryplots/heat-cbar.pdf

19.2 KB
Binary file not shown.

figures/poetryplots/heat-log.pdf

17.7 KB
Binary file not shown.

figures/poetryplots/hockey-heat.pdf

24 KB
Binary file not shown.

figures/poetryplots/mpl-table.pdf

12 KB
Binary file not shown.

figures/poetryplots/speedometer.pdf

8.5 KB
Binary file not shown.

figures/poetryplots/speedometers.pdf

10.3 KB
Binary file not shown.

figures/poetryplots/violin-bar.pdf

0 Bytes
Binary file not shown.

figures/poetryplots/violin-cal.pdf

0 Bytes
Binary file not shown.

figures/poetryplots/violin-streak.pdf

0 Bytes
Binary file not shown.

images/.DS_Store

6 KB
Binary file not shown.

images/predators.png

416 KB
Loading

main.pdf

128 KB
Binary file not shown.

main.tex

+4-1
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,11 @@ \chapter{Applications}\label{chapter:poetryapp}
143143
\section{Activity Calendar}
144144
\input{tex/poetry/calendar.tex}
145145

146+
\section{Heatmaps}
147+
\input{tex/poetry/heat.tex}
146148

147-
149+
\section{Speedometer}
150+
\input{tex/poetry/speedometer.tex}
148151

149152

150153
% Special topics

python/.ipynb_checkpoints/Poetry-Figure-Dev-checkpoint.ipynb

+472-25
Large diffs are not rendered by default.

python/Poetry-Figure-Dev.ipynb

+463-16
Large diffs are not rendered by default.

python/heat-basic.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fig, ax = plt.figure(figsize = (10,3)), plt.axes()
2+
ax.imshow(df.T,
3+
aspect = 20,
4+
cmap = 'Oranges')
5+
ax.set_title("Google Search Trends")

python/heat-cbar.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
fig, ax = plt.subplots(figsize = (10,5))
2+
s = ax.imshow(df.T,
3+
aspect = 20,
4+
cmap = 'Oranges',
5+
vmax = 50)
6+
7+
thinning = 12
8+
ax.set_xticks(range(0,len(df),thinning))
9+
ax.set_xticklabels(list(df.T)[::thinning],
10+
rotation = 30,
11+
ha = 'left')
12+
13+
ax.set_yticks(range(2))
14+
ax.set_yticklabels(df.T.index)
15+
ax.xaxis.set_tick_params(labeltop = True,
16+
labelbottom = False,
17+
labelsize = 10,
18+
tick2On= True,
19+
tick1On = True)
20+
cbar = fig.colorbar(mappable = s,
21+
ax = ax,
22+
orientation = 'horizontal',
23+
pad = 0.04,
24+
extend = 'max')
25+
26+
ax.set_title("Google Search Trends",
27+
size = 18,
28+
pad = 12,
29+
loc = 'center')

python/heat-log.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fig, ax = plt.subplots(figsize = (10,3))
2+
ax.imshow(df.T,
3+
aspect = 20,
4+
cmap = 'Oranges',
5+
norm = mpl.colors.LogNorm())
6+
ax.set_title("Google Search Trends")
7+
8+
thinning = 12 # label every 12th month
9+
ax.set_xticks(range(0,len(df),thinning))
10+
ax.set_xticklabels(list(df.T)[::thinning],
11+
rotation = 30)
12+
ax.set_yticks(range(2))
13+
ax.set_yticklabels(df.T.index)

python/hockey-heat.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from PIL import Image
2+
3+
fig, ax = plt.subplots(figsize = (10,10), facecolor = (.98, .98, .98))
4+
teams = 'ABCDEF'
5+
years = range(2002,2009)
6+
ax.set_aspect('equal')
7+
ax.axis('off')
8+
9+
y_pad = -1
10+
for team_key, team in enumerate(teams):
11+
for year_key, year in enumerate(years):
12+
13+
year_str = str(year)[-2:] + "-" + str(year+1)[-2:]
14+
15+
record = min(1,np.random.random()**(team_key+1) + .05)
16+
if year != 2004:
17+
circ = plt.Circle((year_key, y_pad*team_key),
18+
radius = .25 + (record/2),
19+
color = 'C0',
20+
alpha = record)
21+
ax.add_artist(circ)
22+
ax.text(year_key, y_pad*team_key,
23+
s = str(round(100*record))+"%",
24+
va= 'center',
25+
ha = 'center')
26+
27+
if year_key == 0:
28+
ax.text(year_key - 1, y_pad*team_key,
29+
s = team,
30+
ha = 'right',
31+
va= 'center')
32+
if team_key == 0:
33+
ax.text(year_key, y_pad*team_key + 1,
34+
s = year_str,
35+
ha = 'center',
36+
va= 'bottom')
37+
38+
ax.set_ylim(y_pad*5 - 1, 1)
39+
ax.set_xlim(-1.5, len(years))
40+
41+
# lockout annotation
42+
string = 'No Season Due To Lockout'
43+
ax.text(2, 0,
44+
s = '\n'.join(string),
45+
va= 'top',
46+
ha = 'center',
47+
size = 15)
48+
49+
# Add Logo
50+
file = '../../images/predators.png'
51+
img = np.asarray(Image.open(file))
52+
off_img = mpl.offsetbox.OffsetImage(img, zoom=.016)
53+
ab = mpl.offsetbox.AnnotationBbox(off_img, (-1, 0),
54+
xycoords='data',
55+
frameon = False)
56+
#ax.add_artist(ab)
57+
ax.text(.5, 1.05,
58+
s = "Hockey Regular Season Records",
59+
size = 20,
60+
ha = 'center',
61+
va = 'bottom',
62+
transform = ax.transAxes)

python/mpl-table.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
url = 'https://github.com/alexanderthclark/MPL-Data/raw/main/HopperOstromTrends.csv'
2+
df = pd.read_csv(url, index_col = 'Month')
3+
fig, ax = plt.subplots()
4+
ax.axis('off')
5+
n = 10 # how many rows
6+
ax.table(cellText = df.head(n).values,
7+
rowLabels = df.head(n).index,
8+
colLabels = list(df),
9+
loc = 'center')

python/speedo-functions.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
def rotation(theta):
2+
"""Construct rotation matrix for angle theta in radians."""
3+
rotation_matrix = np.matrix([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
4+
return rotation_matrix
5+
6+
7+
def speedometer(percentile, ax = None, fsize = 20):
8+
"""Constructor speedometer plot along the half circle from 0 to pi."""
9+
# Make half circle from 0 to pi
10+
angles = np.linspace(0, np.pi, 100)
11+
x = np.cos(angles)
12+
y = np.sin(angles)
13+
if ax is None:
14+
ax = plt.gca()
15+
ax.set_aspect('equal')
16+
ax.axis('off')
17+
ax.plot(x,y, linewidth = 3, color = 'black')
18+
19+
# Calculate angle for percentile
20+
theta = -np.pi * (percentile/100)
21+
22+
# Draw hand initialized at 180 degrees
23+
length = 0.8
24+
base = np.matrix([0,0])
25+
tip = np.matrix([-length,0])
26+
points = [base,tip]
27+
new_points = [rotation(theta)*p.T for p in points]
28+
x = [np.array(p).flatten()[0] for p in new_points]
29+
y = [np.array(p).flatten()[1] for p in new_points]
30+
31+
ax.plot(x,y, color = 'darkred',
32+
linewidth = 4, solid_capstyle = 'round')
33+
ax.plot(0,0, marker = 'o',
34+
color = 'darkred', markersize = 10)
35+
36+
# Mark every 10pp
37+
ticks = np.linspace(0,180, 11)
38+
for angle in ticks: #np.arange(0,181, step = step):
39+
radians = math.radians(angle)
40+
41+
# tick line
42+
x1, y1 = np.cos(radians), np.sin(radians)
43+
x2, y2 = .95*x1, .95*y1
44+
ax.plot([x1,x2], [y1,y2],
45+
color = 'black', zorder = -1)
46+
47+
# place text label by tick
48+
raw = 180 - angle
49+
ptile = raw/180 * 100
50+
s = "{:.0f}%".format(ptile)
51+
ax.text(.9*x2, .9*y2, s,
52+
ha = 'center', zorder = -1)
53+
54+
# ghost in the value bc why not
55+
# edit to include raw value or whatever desired
56+
ax.text(0.04, .15, '{:.0f}%'.format(percentile),
57+
va= 'bottom',
58+
ha = 'center',
59+
size = fsize,
60+
alpha = 0.2)

python/speedometer.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
speedometer(100, fsize = 65)

python/speedometers.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fig, axx = plt.subplots(1,3, figsize = (10,3))
2+
3+
for ax, p in zip(axx, [20, 50, 80]):
4+
speedometer(p, ax = ax, fsize = 33)

tex/poetry/calendar.tex

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
You might have in mind ways to spice up this plot even further. Maybe there should be partial credit given to days some with violin practice, but not at least 30 minutes. Perhaps on those days, the circles can be filled with a light gray. The key here is that we're creating a plot that we might think will be more motivating.
2020

21-
Apps like Duolingo or Peloton gamify the user experience, and part of that is rewarding activity streaks over consecutive days. Let's try to enhance the plot so that streaks stand out. We'll accomplish this by adding a yellow edge color and increasing its weight as a streak continues.
21+
Apps like Duolingo or Peloton gamify the user experience, and part of that is rewarding activity streaks over consecutive days. Let's try to enhance the plot so that streaks stand out. We'll accomplish this by adding a yellow edge color and increasing its weight as a streak continues. This adds some visual reward for streaks that was not evident in the previous plot.
2222

2323
\pyfile{violin-streak.py}
2424

tex/poetry/heat.tex

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
% Bubble grid
2+
\subsection{Google Trends}
3+
4+
We have some data in a csv giving Google search trend popularity by month for computer scientist \link{https://en.wikipedia.org/wiki/Grace_Hopper}{Grace Hopper} and Nobel Prize-winning economist \link{https://en.wikipedia.org/wiki/Elinor_Ostrom}{Elinor Ostrom}. We'll try visualizing the trends with a few heatmaps.
5+
6+
First, let's import the data. We can even look at the data in a table with matplotlib if for some sick reason you want to use matplotlib for such a task.
7+
\pyfile{mpl-table.py}
8+
9+
10+
\begin{center}
11+
\includegraphics[width = .8\textwidth]{figures/poetryplots/mpl-table.pdf}
12+
\end{center}
13+
14+
A table is capital-B Boring. A \code{df.plot()} line chart would display these trends very well, but let's move on to making a heatmap. Here we'd have just two lines, but imagine we had several more columns in our dataset. The line chart would become a spaghetti chart. Heatmaps can be useful in avoiding this by giving each data series its own lane. Take this as a toy example of an application that helps when you have that additional clutter.
15+
16+
\begin{center}
17+
\includegraphics[width = .8\textwidth]{figures/poetryplots/df-plot.pdf}
18+
\end{center}
19+
20+
Matplotlib offers the \code{imshow()} axes method especially for this purpose. Later, we'll create our own heatmap simply by adding our own artist objects to the plot.
21+
22+
\subsubsection{Using \code{imshow()}}
23+
24+
First can call \code{imshow()} with our transposed dataframe to get a sense of what the method does. The transpose is done so that the time (the rows of the DataFrame) will be on the $x$-axis. The \code{aspect} argument essentially makes the chart taller. With \code{aspect} set to 20, each cell is 20 times taller than it is wide. Finally, I change the colormap with \code{cmap = 'Oranges'} to create a monochromatic map where a darker orange is a higher search volume.
25+
26+
\pyfile{heat-basic.py}
27+
28+
\begin{center}
29+
\includegraphics[width = .8\textwidth]{figures/poetryplots/heat-basic.pdf}
30+
\end{center}
31+
32+
One obvious problem with this is we have no useful labels or ticks, unlike what we'd get for free with a simple \code{df.plot()} call. This will be remedied with the ticks customizations we learned about in Section \ref{sec:ticks}. Another problem is that our data is not well behaved. The trends are washed out because of an outlier. In December 2013, Grace Hopper was featured in the Google Doodle, driving a lot of searches. All other months pale in comparison. By default, \code{imshow} uses a linear scaling mapping where the lowest value is at the bottom of the colormap and the highest value is at the top of the colormap. As a result almost, all observations are toward the bottom of the colormap. One could transform the data directly or one could change the behavior of \code{imshow()}. We can transform the data with the \code{norm} argument or manually set the top and bottom values of the colormap with \code{vmin} and \code{vmax}.
33+
34+
First we use \code{mpl.colors.LogNorm()} to take a log transform of the data before normalizing. We also thin the $x$-axis ticks manually (instead of using a Formatter) and then relabel the ticks manually.
35+
36+
\pyfile{heat-log.py}
37+
38+
\begin{center}
39+
\includegraphics[width = .8\textwidth]{figures/poetryplots/heat-log.pdf}
40+
\end{center}
41+
42+
The data transformation helps the trends seen in the original line chart pop more prominently. Elinor Ostrom's search interest has remained steady but for a spike in 2009 when she was awarded the Nobel Prize in Economics, along with Oliver Williamson. The surge in search interest for Grace Hopper because of the 2013 Google Doodle remains noticeable, but the yearly spikes in interest around the Grace Hopper conference (usually held around the beginning of October) are more noticeable. Perhaps you find the transformation makes the heatmap too noisy. We can keep the quiet of the original linearly-scaled heatmap, but make but make spikes in interest more visible by lowering the point at which the color gradient maxes out. Below we do this by lowering \code{vmax}. By default, \code{vmax} uses the maximum data value (100 in our case). Setting \code{vmax = 50} means values from 50 to 100 are not differentiated by color in the heatmap. We'll also add a colorbar.
43+
44+
\pyfile{heat-cbar.py}
45+
46+
\begin{center}
47+
\includegraphics[width = .8\textwidth]{figures/poetryplots/heat-cbar.pdf}
48+
\end{center}
49+
50+
51+
\subsection{NHL Regular Season Records}
52+
53+
WIP. Below is a kind of a heatmap.
54+
55+
\pyfile{hockey-heat.py}
56+
57+
\begin{center}
58+
\includegraphics[width = .7\textwidth]{figures/poetryplots/hockey-heat.pdf}
59+
\end{center}

tex/poetry/speedometer.tex

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
There's an allure to control rooms or all the gauges on the dashboard of a vehicle. If you can make your dashboards alluring, your stakeholders will visit them more often. We'll create a simple speedometer-like gauge to add some visual interest to reporting a single percentile value. We'll use what learned about rotating points in Section \ref{subsec:rotations}
2+
The gauge will have values running from 0\% to 100\%, and we'll place these along a half circle. The gauge's hand will point to a particular realized percentile value. We use a rotation matrix to find the correct angle at which to place the hand.
3+
4+
\pyfile{speedo-functions.py}
5+
6+
\pyfile{speedometer.py}
7+
8+
\begin{center}
9+
\includegraphics[width = .75\textwidth]{figures/poetryplots/speedometer.pdf}
10+
\end{center}
11+
12+
You might prefer to make further modifications to the sizing and spacing depending. Below, we see some crowding with the tick labels.
13+
14+
\pyfile{speedometers.py}
15+
16+
\begin{center}
17+
\includegraphics[width = .75\textwidth]{figures/poetryplots/speedometers.pdf}
18+
\end{center}

0 commit comments

Comments
 (0)