Skip to content

Commit 566805b

Browse files
committed
improved fourier layout, updated sfpython talk, upgraded python modules
1 parent 0880977 commit 566805b

11 files changed

+5119
-3116
lines changed

notebooks/00_effective_visualizations.ipynb

Lines changed: 24 additions & 401 deletions
Large diffs are not rendered by default.

notebooks/01_matplotlib_chapter.ipynb

Lines changed: 57 additions & 56 deletions
Large diffs are not rendered by default.

notebooks/02_bokeh_chapter.ipynb

Lines changed: 146 additions & 57 deletions
Large diffs are not rendered by default.

notebooks/03_bokeh_with_streaming_data.ipynb

Lines changed: 191 additions & 336 deletions
Large diffs are not rendered by default.

notebooks/SF_Python_Lightning_Talk.2019_Dec.ipynb

Lines changed: 2248 additions & 0 deletions
Large diffs are not rendered by default.

notebooks/SF_Python_Lightning_Talk.2020_Apr.ipynb

Lines changed: 2169 additions & 0 deletions
Large diffs are not rendered by default.

notebooks/SF_Python_Lightning_Talk_Dec_2019.ipynb

Lines changed: 0 additions & 2260 deletions
This file was deleted.

notebooks/app/fourier_animated/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
plot_width = 1200
3232
plot_height = int(round(plot_width / 2.654867, 0))
3333

34-
filename = join(dirname(__file__), "description_fourier.html")
34+
filename = join(dirname(__file__), "description_fourier_v2.html")
3535
desc_fourier = Div(text=open(filename).read(),
3636
render_as_text=False, width=plot_width)
3737

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<style>
2+
h1 {
3+
margin: 2em 0 0 0;
4+
color: #2e484c;
5+
font-family: 'Julius Sans One', sans-serif;
6+
font-size: 1.8em;
7+
text-transform: uppercase;
8+
}
9+
a:link {
10+
font-weight: bold;
11+
text-decoration: none;
12+
color: #0d8ba1;
13+
}
14+
a:visited {
15+
font-weight: bold;
16+
text-decoration: none;
17+
color: #1a5952;
18+
}
19+
a:hover, a:focus, a:active {
20+
text-decoration: underline;
21+
color: #9685BA;
22+
}
23+
p {
24+
font: "Libre Baskerville", sans-serif;
25+
text-align: justify;
26+
text-justify: inter-word;
27+
}
28+
code {
29+
color: #336699;
30+
}
31+
</style>
32+
33+
<h1>Sighted people have naturally powerful visual perception abilities</h1>
34+
<h2>Fourier Transformation Example</h2>
35+
<p><strong>credit: Bokeh Maintainers! (bokeh.org)</strong></p>
36+
<p><i>https://github.com/bokeh/bokeh/blob/master/examples/app/fourier_animated.py</i></p>
37+
<p><strong>Fourier Transform background: </strong><i>https://jackschaedler.github.io/circles-sines-signals/dft_introduction.html</i></p>
38+
39+
<p>
40+
Effective visualizations can quickly transmit intuitions for complex topics. This example illustrate multiple sin waves and fourier transformations displayed through a Bokeh Server.
41+
</p>
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
''' Show a streaming, updating representation of Fourier Series.
2+
The example was inspired by `this video`_.
3+
Use the ``bokeh serve`` command to run the example by executing:
4+
bokeh serve main.py
5+
at your command prompt. Then navigate to the URL
6+
http://localhost:5006/fourier_animated_v2
7+
in your browser.
8+
.. _this video: https://www.youtube.com/watch?v=LznjC4Lo7lE
9+
'''
10+
from collections import OrderedDict
11+
12+
from os.path import dirname, join
13+
import numpy as np
14+
from numpy import pi
15+
16+
from bokeh.driving import repeat
17+
from bokeh.io import curdoc
18+
from bokeh.layouts import column
19+
from bokeh.models import ColumnDataSource
20+
from bokeh.models import Div
21+
from bokeh.plotting import figure
22+
23+
N = 100
24+
newx = x = np.linspace(0, 2 * pi, N)
25+
shift = 2.2
26+
base_x = x + shift
27+
28+
period = pi / 2
29+
palette = ['#08519c', '#3182bd', '#6baed6', '#bdd7e7']
30+
31+
plot_width = 1200
32+
plot_height = int(round(plot_width / 2.654867, 0))
33+
34+
filename = join(dirname(__file__), "description_fourier_v2.html")
35+
desc_fourier = Div(text=open(filename).read(),
36+
render_as_text=False, width=plot_width)
37+
38+
39+
def new_source():
40+
return dict(
41+
curve=ColumnDataSource(dict(x=[], base_x=[], y=[])),
42+
lines=ColumnDataSource(dict(line_x=[], line_y=[], radius_x=[], radius_y=[])),
43+
circle_point=ColumnDataSource(dict(x=[], y=[], r=[])),
44+
circleds=ColumnDataSource(dict(x=[], y=[]))
45+
)
46+
47+
48+
def create_circle_glyphs(p, color, sources):
49+
p.circle('x', 'y', size=1., line_color=color, color=None, source=sources['circleds'])
50+
p.circle('x', 'y', size=5, line_color=color, color=color, source=sources['circle_point'])
51+
p.line('radius_x', 'radius_y', line_color=color, color=color, alpha=0.5, source=sources['lines'])
52+
53+
54+
def create_plot(foos, title='', r=1, y_range=None, period=pi / 2, cfoos=None):
55+
if y_range is None:
56+
y_range = [-2, 2]
57+
58+
# create new figure
59+
p = figure(title=title, tools="", plot_width=plot_width, plot_height=plot_height, x_range=[-2, 9], y_range=y_range,
60+
sizing_mode="stretch_width")
61+
p.xgrid.bounds = (-2, 2)
62+
p.xaxis.bounds = (-2, 2)
63+
64+
_sources = []
65+
cx, cy = 0, 0
66+
for i, foo in enumerate(foos):
67+
sources = new_source()
68+
get_new_sources(x, foo, sources, cfoos[i], cx, cy, i == 0)
69+
cp = sources['circle_point'].data
70+
cx, cy = cp['x'][0], cp['y'][0]
71+
72+
if i == 0:
73+
# compute the full fourier eq
74+
full_y = sum(foo(x) for foo in foos)
75+
# replace the foo curve with the full fourier eq
76+
sources['curve'] = ColumnDataSource(dict(x=x, base_x=base_x, y=full_y))
77+
# draw the line
78+
p.line('base_x', 'y', color="orange", line_width=2, source=sources['curve'])
79+
80+
if i == len(foos) - 1:
81+
# if it's the last foo let's draw a circle on the head of the curve
82+
sources['floating_point'] = ColumnDataSource({'x': [shift], 'y': [cy]})
83+
p.line('line_x', 'line_y', color=palette[i], line_width=2, source=sources['lines'])
84+
p.circle('x', 'y', size=10, line_color=palette[i], color=palette[i], source=sources['floating_point'])
85+
86+
# draw the circle, radius and circle point related to foo domain
87+
create_circle_glyphs(p, palette[i], sources)
88+
_sources.append(sources)
89+
90+
return p, _sources
91+
92+
93+
def get_new_sources(xs, foo, sources, cfoo, cx=0, cy=0, compute_curve=True):
94+
if compute_curve:
95+
ys = foo(xs)
96+
sources['curve'].data = dict(x=xs, base_x=base_x, y=ys)
97+
98+
r = foo(period)
99+
y = foo(xs[0]) + cy
100+
x = cfoo(xs[0]) + cx
101+
102+
sources['lines'].data = {
103+
'line_x': [x, shift], 'line_y': [y, y],
104+
'radius_x': [0, x], 'radius_y': [0, y]
105+
}
106+
sources['circle_point'].data = {'x': [x], 'y': [y], 'r': [r]}
107+
sources['circleds'].data = dict(
108+
x=cx + np.cos(np.linspace(0, 2 * pi, N)) * r,
109+
y=cy + np.sin(np.linspace(0, 2 * pi, N)) * r,
110+
)
111+
112+
113+
def update_sources(sources, foos, newx, ind, cfoos):
114+
cx, cy = 0, 0
115+
116+
for i, foo in enumerate(foos):
117+
get_new_sources(newx, foo, sources[i], cfoos[i], cx, cy, compute_curve=i != 0)
118+
119+
if i == 0:
120+
full_y = sum(foo(newx) for foo in foos)
121+
sources[i]['curve'].data = dict(x=newx, base_x=base_x, y=full_y)
122+
123+
cp = sources[i]['circle_point'].data
124+
cx, cy = cp['x'][0], cp['y'][0]
125+
126+
if i == len(foos) - 1:
127+
sources[i]['floating_point'].data['x'] = [shift]
128+
sources[i]['floating_point'].data['y'] = [cy]
129+
130+
131+
def update_centric_sources(sources, foos, newx, ind, cfoos):
132+
for i, foo in enumerate(foos):
133+
get_new_sources(newx, foo, sources[i], cfoos[i])
134+
135+
136+
def create_centric_plot(foos, title='', r=1, y_range=(-2, 2), period=pi / 2, cfoos=None):
137+
p = figure(title=title, tools="", plot_width=plot_width, plot_height=plot_height, x_range=[-2, 9], y_range=y_range,
138+
sizing_mode="stretch_width")
139+
p.xgrid.bounds = (-2, 2)
140+
p.xaxis.bounds = (-2, 2)
141+
142+
_sources = []
143+
for i, foo in enumerate(foos):
144+
sources = new_source()
145+
get_new_sources(x, foo, sources, cfoos[i])
146+
_sources.append(sources)
147+
148+
if i:
149+
legend_label = "4sin(%(c)sx)/%(c)spi" % {'c': i * 2 + 1}
150+
else:
151+
legend_label = "4sin(x)/pi"
152+
153+
p.line('base_x', 'y', color=palette[i], line_width=2, source=sources['curve'])
154+
p.line('line_x', 'line_y', color=palette[i], line_width=2,
155+
source=sources['lines'], legend_label=legend_label)
156+
157+
create_circle_glyphs(p, palette[i], sources)
158+
159+
p.legend.location = "top_right"
160+
p.legend.orientation = "horizontal"
161+
p.legend.padding = 6
162+
p.legend.margin = 6
163+
p.legend.spacing = 6
164+
165+
return p, _sources
166+
167+
168+
# create the series partials
169+
def f1(x):
170+
return (4 * np.sin(x)) / pi
171+
172+
173+
def f2(x):
174+
return (4 * np.sin(3 * x)) / (3 * pi)
175+
176+
177+
def f3(x):
178+
return (4 * np.sin(5 * x)) / (5 * pi)
179+
180+
181+
def f4(x):
182+
return (4 * np.sin(7 * x)) / (7 * pi)
183+
184+
185+
def cf1(x):
186+
return (4 * np.cos(x)) / pi
187+
188+
189+
def cf2(x):
190+
return (4 * np.cos(3 * x)) / (3 * pi)
191+
192+
193+
def cf3(x):
194+
return (4 * np.cos(5 * x)) / (5 * pi)
195+
196+
197+
def cf4(x):
198+
return (4 * np.cos(7 * x)) / (7 * pi)
199+
200+
201+
fourier = OrderedDict(
202+
fourier_4={
203+
'f': lambda x: f1(x) + f2(x) + f3(x) + f4(x),
204+
'fs': [f1, f2, f3, f4],
205+
'cfs': [cf1, cf2, cf3, cf4]
206+
},
207+
)
208+
209+
for k, p in fourier.items():
210+
p['cplot'], p['csources'] = create_centric_plot(
211+
p['fs'], 'Fourier Sine Waves - First 4 Harmonics & Harmonic Circles', r=p['f'](period), cfoos=p['cfs']
212+
)
213+
214+
for k, p in fourier.items():
215+
p['plot'], p['sources'] = create_plot(
216+
p['fs'], 'Fourier Transformation -- (Sum of the first 4 Harmonic Circles)', r=p['f'](period), cfoos=p['cfs']
217+
)
218+
219+
layout = column(*[f['cplot'] for f in fourier.values()] + [f['plot'] for f in fourier.values()])
220+
221+
222+
@repeat(range(N))
223+
def cb(gind):
224+
global newx
225+
oldx = np.delete(newx, 0)
226+
newx = np.hstack([oldx, [oldx[-1] + 2 * pi / N]])
227+
228+
for k, p in fourier.items():
229+
update_centric_sources(p['csources'], p['fs'], newx, gind, p['cfs'])
230+
update_sources(p['sources'], p['fs'], newx, gind, p['cfs'])
231+
232+
233+
curdoc().title = "Fourier Animated"
234+
curdoc().add_root(desc_fourier)
235+
curdoc().add_root(layout)
236+
237+
curdoc().add_periodic_callback(cb, 100)

requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# supporting libraries
22
black==19.3b0
3-
bokeh==2.0.0
4-
dask[complete]==2.13.0
3+
bokeh==2.0.1
4+
dask[complete]==2.14.0
55
datashader==0.10.0
6-
holoviews==1.13.1
6+
holoviews==1.13.2
77
hvplot==0.5.2
88
matplotlib==3.2.1
99
missingno==0.4.2
@@ -12,13 +12,13 @@ numpy==1.18.2
1212
openpyxl==3.0.3
1313
pandas==1.0.3
1414
pandas-bokeh==0.4.2
15-
pillow==7.0.0
15+
pillow==7.1.1
1616
psutil==5.7.0
1717
pur==5.3.0
1818
rise==5.6.1
1919
seaborn==0.10.0
2020
scikit-learn==0.22.2.post1
21-
streamz==0.5.2
21+
streamz==0.5.3
2222
watermark==2.0.2
2323
xlrd==1.2.0
2424

0 commit comments

Comments
 (0)