├── README.md ├── anscombe.py ├── catalogue.png ├── data-ink.gif ├── edward-tufte.png ├── exercise-1-sol.png ├── exercise-1-sol.py ├── exercise-1.png ├── exercise-1.py ├── exercise-2-sol.py ├── exercise-2-sol.sh ├── exercise-3-sol.png ├── exercise-3-sol.py ├── exercise-4-sol.md ├── exercise-4-sol.py ├── exercise-5-sol.png ├── exercise-5-sol.py ├── final.jpg ├── neurons.jpg ├── obama.jpg ├── phd.jpg ├── slides.pdf └── trust.jpg /README.md: -------------------------------------------------------------------------------- 1 | # Scientific visualization 2 | A 2h30 crash course on scientific visualization... 3 | [Nicolas P. Rougier](http://www.labri.fr/perso/nrougier), 4 | [G-Node summer school](https://python.g-node.org/), 5 | University of Reading, 2016. 6 | 7 | 8 | [![](trust.jpg)](http://velica.deviantart.com/art/Error-bars-101948712) 9 | 10 | 11 | "*Visualisation is a method of computing. It transforms the symbolic into the geometric, enabling researchers to observe their simulations and computations. Visualisation offers a method for seeing the unseen. It enriches the process of scientific discovery and fosters profound and unexpected insights.*" 12 | 13 | Visualisation in Scientific Computing, NSF report, 1987. 14 | 15 | ## Introduction 16 | 17 | Scientific visualization is classically defined as the process of graphically 18 | displaying scientific data. However, this process is far from direct or 19 | automatic. There are so many different ways to represent the same data: scatter 20 | plots, linear plots, bar plots, and pie charts, to name just a 21 | few. Furthermore, the same data, using the same type of plot, may be perceived 22 | very differently depending on who is looking at the figure. A more accurate 23 | definition for scientific visualization would be a graphical interface between 24 | people and data. But remember, there are two people in the loop: the one that 25 | produces the visualization and the one that watches it. What you intend to show 26 | might be quite different from what will be actually perceived... 27 | 28 | The goal of this crash course is to introduce a few concepts in order for you 29 | to achieve better visualization (hopefully). If you want to go further, you'll 30 | have to look at the miscellaneous references given at the end of this document. 31 | 32 | 33 | ## Visualization pipeline 34 | 35 | The visualization pipeline describes the process of creating visual 36 | representations of data, from the raw data up to the final rendering. There is 37 | no unique definition of such pipeline but most of the time you'll find at least 38 | 3 steps (filter, map, render). 39 | 40 | 1. Raw data (whatever...) 41 | 2. Filtered data (missing, noise, analytics, statistics, ...) 42 | 3. Mapped data (geometry, attributes, colors, ...) 43 | 4. Rendered data (static image, interactive display, ...) 44 | 45 | 46 | 47 | ## Data type 48 | 49 | The nature of the data has a great influence on the kind of visualization you 50 | can use. Traditionally, they are split as: 51 | 52 | **Quantitative** (values or observations that can be measured) 53 | 54 | * Continuous 55 | * Discrete 56 | 57 | **Categorical** (values or observations that can be sorted into groups or 58 | categories) 59 | 60 | * Nominal 61 | * Ordinal 62 | * Interval 63 | 64 | but you can also find finer detailed descriptions in the litterature. 65 | 66 | 67 | 68 | ## Graphical elements 69 | 70 | In the end, a scientific figures can be fully described by a set of 71 | graphic primitives with different attributes: 72 | 73 | * Points, markers, lines, areas, ... 74 | * Position, color, shape, size, orientation, curvature, ... 75 | * Helpers, text, axis, ticks, 76 | * Interaction, animation 77 | 78 | But describing a figure in terms of such graphic primitive would be a very 79 | tedious and complex task. This is exactly where visualization libraries or 80 | software are useful because they will automatize most of the work, more 81 | (e.g. [seaborn](https://stanford.edu/~mwaskom/software/seaborn/)) or less 82 | (e.g. [matplotlib](http://matplotlib.org)) depending on the library. In the 83 | ideal case, you want to only specify your data and let the library decides of almost everything (e.g. [vega-lite](https://vega.github.io/vega-lite/)) 84 | 85 | 86 | ## Visualization type 87 | ![](catalogue.png) 88 | 89 | From the [Data visualization catalogue](http://www.datavizcatalogue.com/index.html) by Severino Ribecca. 90 | 91 | 92 | 93 | ## Less is more 94 | 95 | *Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away* – Antoine de Saint-Exupery 96 | 97 | ![](data-ink.gif) 98 | 99 | 100 | 101 | ## Ten simple rules 102 | 103 | From [Ten simple rules for better figures](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1003833), N.P. Rougier, M. Droettboom, P.E. Bourne, 2014. 104 | 105 | 106 | 1. Know your audience 107 | 2. Identify Your Message 108 | 3. Adapt the Figure to the Support Medium 109 | 4. Captions Are Not Optional 110 | 5. Do Not Trust the Defaults 111 | 6. Use Color Effectively 112 | 7. Do Not Mislead the Reader 113 | 8. Avoid “Chartjunk” 114 | 9. Message Trumps Beauty 115 | 10. Get the Right Tool 116 | 117 | See also https://github.com/rougier/ten-rules 118 | 119 | 120 | ## Exercices 121 | 122 | ### Exercise 1: Too much ink... 123 | 124 | Consider the following figure and, using matplotlib, try to remove as much ink 125 | as you can while keeping the most relevant information. 126 | 127 | ![](exercise-1.png) 128 | 129 | You can start from the following python script: 130 | 131 | 132 | ``` 133 | import numpy as np 134 | import matplotlib.pyplot as plt 135 | 136 | np.random.seed(123) 137 | 138 | def gaussian(x, a, x0, sigma): 139 | return a*np.exp(-(x-x0)**2/(2*sigma**2)) 140 | 141 | # Clean data 142 | X = np.linspace(15, 21, 100) 143 | Y = gaussian(X, 0.65, 17.6, 1.) 144 | 145 | # Noisy dat 146 | Xn = np.random.uniform(16, 20, 25) 147 | Yn = gaussian(Xn, 0.65, 17.6, 1.) + 0.01 * np.random.normal(size=len(Xn)) 148 | ``` 149 | 150 | ### Exercise 2: Using the right tool 151 | 152 | You have a nice [image](neurons.jpg) and you would like to show labeled 153 | detailed sub-images alongside the main image (see below). What could be the 154 | easiest way to do that ? Be careful with the labels, they must be visible 155 | independently of the images color/contrast. 156 | 157 | ![](final.jpg) 158 | 159 | 160 | ### Exercise 3: Misleading the reader 161 | 162 | What's wrong with this graphic ? How would you correct it ? 163 | 164 | ![](obama.jpg) 165 | 166 | ### Exercise 4: Editor request 167 | 168 | Your article just been accepted but the editor request figure 2 to be at least 169 | 300 dpi. What does that mean ? What is the minium size (in pixels) of your 170 | figure ? Is it relevant if you figure has been saved in vector format ? 171 | 172 | 173 | ### Exercise 5: Replication 174 | 175 | Look at 176 | [Drawing a brain with Bokeh](http://merqur.io/2015/10/02/drawing-a-brain-with-bokeh/) 177 | and try to replicate the final figure using matpltolib. 178 | 179 | **or** 180 | 181 | Pick one of your favorite graphic from the litterature and try to replicate it 182 | using matplotlib (and fake data). 183 | 184 | 185 | 186 | ## References 187 | 188 | There are any online resources about scientific visualization and a lot of 189 | excellent books as well. Since you probably not have time to read everything, I 190 | collected a small set of resources that might be read relatively rapidly. 191 | 192 | **Courses/Tutorials/Guides** 193 | 194 | * [Matplotlib tutorial](http://www.labri.fr/perso/nrougier/teaching/matplotlib/matplotlib.html), N.P. Rougier, 2016. 195 | * [Ten simple rules for better figures](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1003833), 196 | N.P. Rougier, M. Droettboom, P.E. Bourne, 2014. 197 | * [Scientific Visualization course](http://www.cspaul.com/wordpress/course-vis-2015/), Paul Rosen, 2015. 198 | * [Information Visualization](http://www.cs.ubc.ca/~tmm/courses/infovis/slides/intro.pdf), T. Munzner, 2015. 199 | * [The Quartz guide to bad data](https://github.com/Quartz/bad-data-guide), 200 | C. Groskopf, 2015. 201 | * [Quantitative vs. Categorical Data: A Difference Worth Knowing](https://www.perceptualedge.com/articles/dmreview/quant_vs_cat_data.pdf), S. Few , 2005. 202 | * [How to make beautiful data visualizations in Python with matplotlib](http://www.randalolson.com/2014/06/28/how-to-make-beautiful-data-visualizations-in-python-with-matplotlib/), Randy Olson, 2014. 203 | 204 | **(Some) Tools** 205 | 206 | * [Matplotlib](http://www.aosabook.org/en/matplotlib.html), 207 | J. Hunter and M. Droettboom, 2010. 208 | * [10 Useful Python Data Visualization Libraries for Any Discipline](https://blog.modeanalytics.com/python-data-visualization-libraries/), M. Bierly, 2016. 209 | * [Datavisualization.ch](http://selection.datavisualization.ch), 2015. 210 | * [Data visualization catalogue](http://www.datavizcatalogue.com/index.html), 211 | S. Ribecca, 2016. 212 | * [Fred's ImageMagick script](http://www.fmwconcepts.com/imagemagick/), F. Weinhaus, 2016. 213 | * [Ti*k*Z and PGF](http://www.texample.net/tikz/), Stefan Kottwitz 214 | 215 | **Books** 216 | 217 | * [Visualization Analysis and Design](http://www.cs.ubc.ca/~tmm/vadbook/), 218 | T. Munzner, 2014. 219 | * [Trees, maps, and theorems](http://www.treesmapsandtheorems.com), 220 | J.-L. Doumont, 2009. 221 | * [The Visual Display of Quantitative Information](https://www.edwardtufte.com/tufte/books_vdqi), E.R. Tufte, 1983. 222 | 223 | **Good examples** 224 | 225 | * [A Tour through the Visualization Zoo](http://queue.acm.org/detail.cfm?id=1805128), J. Heer, M. Bostock, and V. Ogievetsky, 2010. 226 | * [The most misleading charts of 2015, fixed](http://qz.com/580859/the-most-misleading-charts-of-2015-fixed/), K. Collins, 2015. 227 | * [Data is beautiful](https://www.reddit.com/r/dataisbeautiful/) / reddit. 228 | 229 | 230 | **Bad examples (don't do that at home)** 231 | 232 | * [Junk charts](http://junkcharts.typepad.com), K. Fung, 2005-2016. 233 | * [WTF Visualizations](http://viz.wtf), community supported. 234 | * [How to Display Data Badly](http://www.jstor.org/stable/2683253), H. Wainer, 1984. 235 | 236 | 240 | 241 | ---- 242 | 243 | 244 | ## Solutions to the exercises 245 | 246 | 1. [exercise-1-sol.py](exercise-1-sol.py) / [exercise-1-sol.png](exercise-1-sol.png) 247 | (adapted from "Trees, maps, and theorems") 248 | 2. [exercise-2-sol.sh](exercise-2-sol.sh) or [exercise-2-sol.py](exercise-2-sol.py) 249 | 3. [exercise-3-sol.py](exercise-3-sol.py) / [exercise-3-sol.png](exercise-3-sol.png) 250 | (adapted from "The most misleading charts of 2015, fixed") 251 | 4. [exercise-4-sol.md](exercise-4-sol.md) or [exercise-4-sol.py](exercise-4-sol.py) 252 | 5. [exercise-5-sol.py](exercise-5-sol.py) / [exercise-5-sol.png](exercise-5-sol.png) 253 | 254 | ![](edward-tufte.png) 255 | -------------------------------------------------------------------------------- /anscombe.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | x = np.array([10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]) 6 | y1 = np.array([8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]) 7 | y2 = np.array([9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]) 8 | y3 = np.array([7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]) 9 | x4 = np.array([8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8]) 10 | y4 = np.array([6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]) 11 | 12 | 13 | def fit(x): 14 | return 3 + 0.5*x 15 | 16 | 17 | fig = plt.figure(figsize=(10,8)) 18 | fig.patch.set_facecolor('white') 19 | 20 | xfit = np.array([np.amin(x), np.amax(x)]) 21 | 22 | ax = plt.subplot(221) 23 | plt.plot(xfit, fit(xfit), '.75', lw=1, zorder=-10) 24 | plt.scatter(x, y1, s=50, edgecolor="k", facecolor="w", linewidth=2, zorder=10) 25 | plt.xlim(2,20), plt.xticks(np.arange(4,20,2)) 26 | plt.ylim(2,14), plt.yticks(np.arange(4,14,2)) 27 | plt.title('What we expect...', loc='left', color='r', fontsize=16) 28 | 29 | ax.spines['right'].set_color('none') 30 | ax.spines['top'].set_color('none') 31 | ax.xaxis.set_ticks_position('bottom') 32 | ax.spines['bottom'].set_position(('data', 1)) 33 | ax.spines['bottom'].set_color('0.5') 34 | ax.yaxis.set_ticks_position('left') 35 | ax.spines['left'].set_position(('data', 1)) 36 | ax.spines['left'].set_color('0.5') 37 | [t.set_color('0.5') for t in ax.xaxis.get_ticklines()] 38 | [t.set_color('0.5') for t in ax.xaxis.get_ticklabels()] 39 | [t.set_color('0.5') for t in ax.yaxis.get_ticklines()] 40 | [t.set_color('0.5') for t in ax.yaxis.get_ticklabels()] 41 | 42 | 43 | ax = plt.subplot(222) 44 | plt.plot(xfit, fit(xfit), '.75', lw=1, zorder=-10) 45 | plt.scatter(x, y2, s=50, edgecolor="k", facecolor="w", linewidth=2, zorder=10) 46 | plt.xlim(2,20), plt.xticks(np.arange(4,20,2)) 47 | plt.ylim(2,14), plt.yticks(np.arange(4,14,2)) 48 | plt.title('The non-linear case', loc='left', color='r', fontsize=16) 49 | 50 | ax.spines['right'].set_color('none') 51 | ax.spines['top'].set_color('none') 52 | ax.xaxis.set_ticks_position('bottom') 53 | ax.spines['bottom'].set_position(('data', 1)) 54 | ax.spines['bottom'].set_color('0.5') 55 | ax.yaxis.set_ticks_position('left') 56 | ax.spines['left'].set_position(('data', 1)) 57 | ax.spines['left'].set_color('0.5') 58 | [t.set_color('0.5') for t in ax.xaxis.get_ticklines()] 59 | [t.set_color('0.5') for t in ax.xaxis.get_ticklabels()] 60 | [t.set_color('0.5') for t in ax.yaxis.get_ticklines()] 61 | [t.set_color('0.5') for t in ax.yaxis.get_ticklabels()] 62 | 63 | 64 | ax = plt.subplot(223) 65 | plt.plot(xfit, fit(xfit), '.75', lw=1, zorder=-10) 66 | plt.scatter(x, y3, s=50, edgecolor="k", facecolor="w", linewidth=2, zorder=10) 67 | plt.xlim(2,20), plt.xticks(np.arange(4,20,2)) 68 | plt.ylim(2,14), plt.yticks(np.arange(4,14,2)) 69 | plt.title('The Y outlier case', loc='left', color='r', fontsize=16) 70 | 71 | ax.spines['right'].set_color('none') 72 | ax.spines['top'].set_color('none') 73 | ax.xaxis.set_ticks_position('bottom') 74 | ax.spines['bottom'].set_position(('data', 1)) 75 | ax.spines['bottom'].set_color('0.5') 76 | ax.yaxis.set_ticks_position('left') 77 | ax.spines['left'].set_position(('data', 1)) 78 | ax.spines['left'].set_color('0.5') 79 | [t.set_color('0.5') for t in ax.xaxis.get_ticklines()] 80 | [t.set_color('0.5') for t in ax.xaxis.get_ticklabels()] 81 | [t.set_color('0.5') for t in ax.yaxis.get_ticklines()] 82 | [t.set_color('0.5') for t in ax.yaxis.get_ticklabels()] 83 | 84 | 85 | ax = plt.subplot(224) 86 | xfit = np.array([np.amin(x4), np.amax(x4)]) 87 | plt.plot(xfit, fit(xfit), '.75', lw=1, zorder=-10) 88 | plt.scatter(x4, y4, s=50, edgecolor="k", facecolor="w", linewidth=2, zorder=10) 89 | plt.xlim(2,20), plt.xticks(np.arange(4,20,2)) 90 | plt.ylim(2,14), plt.yticks(np.arange(4,14,2)) 91 | plt.title('The X outlier case', loc='left', color='r', fontsize=16) 92 | 93 | ax.spines['right'].set_color('none') 94 | ax.spines['top'].set_color('none') 95 | ax.xaxis.set_ticks_position('bottom') 96 | ax.spines['bottom'].set_position(('data', 1)) 97 | ax.spines['bottom'].set_color('0.5') 98 | ax.yaxis.set_ticks_position('left') 99 | ax.spines['left'].set_position(('data', 1)) 100 | ax.spines['left'].set_color('0.5') 101 | [t.set_color('0.5') for t in ax.xaxis.get_ticklines()] 102 | [t.set_color('0.5') for t in ax.xaxis.get_ticklabels()] 103 | [t.set_color('0.5') for t in ax.yaxis.get_ticklines()] 104 | [t.set_color('0.5') for t in ax.yaxis.get_ticklabels()] 105 | 106 | 107 | plt.tight_layout() 108 | plt.savefig("anscombe.pdf") 109 | plt.show() 110 | -------------------------------------------------------------------------------- /catalogue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/catalogue.png -------------------------------------------------------------------------------- /data-ink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/data-ink.gif -------------------------------------------------------------------------------- /edward-tufte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/edward-tufte.png -------------------------------------------------------------------------------- /exercise-1-sol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/exercise-1-sol.png -------------------------------------------------------------------------------- /exercise-1-sol.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 3 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 4 | # ----------------------------------------------------------------------------- 5 | # Scientific visualization course 6 | # G-Node 2016 summer school, University of Reading 7 | # ----------------------------------------------------------------------------- 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | def gaussian(x, a, x0, sigma): 12 | return a*np.exp(-(x-x0)**2/(2*sigma**2)) 13 | 14 | np.random.seed(123) 15 | 16 | # Clean data 17 | X = np.linspace(15, 21, 100) 18 | Y = gaussian(X, 0.65, 17.6, 1.) 19 | 20 | # Noisy dat 21 | Xn = np.random.uniform(16, 20, 25) 22 | Yn = gaussian(Xn, 0.65, 17.6, 1.) + 0.01 * np.random.normal(size=len(Xn)) 23 | 24 | plt.figure(figsize=(8,6), facecolor="w") 25 | ax = plt.subplot(121) 26 | plt.plot(X, 1000*Y, color='orange', linewidth=1.5, zorder=-10, 27 | clip_on=False) 28 | plt.scatter(Xn, 1000*Yn, s=50, marker=".", color="black") 29 | 30 | 31 | ax.spines['right'].set_color('none') 32 | ax.spines['top'].set_color('none') 33 | ax.xaxis.set_ticks_position('bottom') 34 | ax.spines['bottom'].set_position(('axes', -0.1)) 35 | ax.spines['bottom'].set_color('0.5') 36 | ax.yaxis.set_ticks_position('left') 37 | ax.spines['left'].set_position(('axes', -0.5)) 38 | ax.spines['left'].set_color('0.5') 39 | 40 | plt.xlim(16,20) 41 | plt.ylim(0,650) 42 | 43 | plt.xticks([16,17.6,20],["16", "17.6 GHz", "20"]) 44 | plt.yticks([0, 325, 650], ["0", "", "650 mW"]) 45 | 46 | plt.tick_params(which='major', width=1.5, color="0.5") 47 | 48 | ax.text(-0.5, 1.02, 'Output power', 49 | verticalalignment='bottom', horizontalalignment='right', 50 | transform=ax.transAxes, color='0.5', fontsize=12) 51 | ax.text(18, 630, 'Measured', 52 | transform=ax.transData, color='black', fontsize=12) 53 | ax.text(18.5, 500, 'Calculated', 54 | transform=ax.transData, color='orange', fontsize=12) 55 | 56 | plt.tight_layout(pad=2.0) 57 | 58 | [t.set_color('0.5') for t in ax.xaxis.get_ticklines()] 59 | [t.set_color('0.5') for t in ax.xaxis.get_ticklabels()] 60 | [t.set_color('0.5') for t in ax.yaxis.get_ticklines()] 61 | [t.set_color('0.5') for t in ax.yaxis.get_ticklabels()] 62 | 63 | plt.savefig("exercise-1-sol.png") 64 | plt.show() 65 | -------------------------------------------------------------------------------- /exercise-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/exercise-1.png -------------------------------------------------------------------------------- /exercise-1.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 3 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 4 | # ----------------------------------------------------------------------------- 5 | # Scientific visualization course 6 | # G-Node 2016 summer school, University of Reading 7 | # ----------------------------------------------------------------------------- 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | from matplotlib.ticker import MultipleLocator 11 | 12 | np.random.seed(123) 13 | 14 | def gaussian(x, a, x0, sigma): 15 | return a*np.exp(-(x-x0)**2/(2*sigma**2)) 16 | 17 | # Clean data 18 | X = np.linspace(15, 21, 100) 19 | Y = gaussian(X, 0.65, 17.6, 1.) 20 | 21 | # Noisy dat 22 | Xn = np.random.uniform(16, 20, 25) 23 | Yn = gaussian(Xn, 0.65, 17.6, 1.) + 0.01 * np.random.normal(size=len(Xn)) 24 | 25 | 26 | plt.figure(figsize=(6,6)) 27 | plt.plot(X, Y, c='k', label="calculated") 28 | plt.scatter(Xn, Yn, s=250, marker="+", color="k", label="measured") 29 | plt.xlim(15,21) 30 | plt.ylim(0,1) 31 | plt.grid(linestyle="-") 32 | plt.xticks(np.linspace(15,21,7)) 33 | plt.yticks(np.linspace(0,1,11)) 34 | plt.legend() 35 | 36 | plt.ylabel("Output power [W]") 37 | plt.xlabel("Frequency [GHz]") 38 | 39 | plt.tick_params(which='major', width=1.5) 40 | plt.tick_params(which='minor', length=10) 41 | plt.tick_params(which='major', length=20) 42 | plt.axes().xaxis.set_minor_locator(MultipleLocator(0.10)) 43 | plt.axes().yaxis.set_minor_locator(MultipleLocator(0.01)) 44 | 45 | 46 | plt.savefig("exercise-1.png") 47 | 48 | plt.show() 49 | -------------------------------------------------------------------------------- /exercise-2-sol.py: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 4 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 5 | # ----------------------------------------------------------------------------- 6 | # Scientific visualization course 7 | # G-Node 2016 summer school, University of Reading 8 | # ----------------------------------------------------------------------------- 9 | import numpy as np 10 | from scipy import misc 11 | import matplotlib 12 | import matplotlib.pyplot as plt 13 | import matplotlib.patches as patches 14 | import matplotlib.patheffects as path_effects 15 | 16 | matplotlib.rc('axes',edgecolor='w') 17 | 18 | # We load the image to get its dimensions 19 | im = misc.imread("neurons.jpg") 20 | 21 | # Read image dimensions as lines (height), cols (width) and depth 22 | h,w,_ = im.shape 23 | 24 | # Now we open a new figure with size 2xw, 2xh 25 | dpi = 72.0 26 | W, H= 2*w/dpi, 2*h/dpi 27 | fig = plt.figure(figsize=(W,H), dpi=dpi) 28 | 29 | # Main figure 30 | ax1 = plt.axes([0.0, 0.5, 0.5, 0.5]) 31 | ax1.imshow(im, origin='upper') 32 | patch = patches.Rectangle((100,170),100,70, facecolor='none', edgecolor='white') 33 | ax1.add_patch(patch) 34 | ax1.text(105, 175, 'A', fontsize=12, va="top", color="white") 35 | patch = patches.Rectangle((200, 20),100,70, facecolor='none', edgecolor='white') 36 | ax1.add_patch(patch) 37 | ax1.text(205, 25, 'B', fontsize=12, va="top", color="white") 38 | patch = patches.Rectangle((270,200),100,70, facecolor='none', edgecolor='white') 39 | ax1.add_patch(patch) 40 | ax1.text(275, 205, 'C', fontsize=12, va="top", color="white") 41 | 42 | ax1.set_xticks([]) 43 | ax1.set_yticks([]) 44 | #ax1.set_axis_off() 45 | 46 | # Subplot A 47 | ax2 = plt.axes([0.5, 0.5, 0.5, 0.5]) 48 | ax2.imshow(im, interpolation='none') 49 | ax2.set_xlim(100,200) 50 | ax2.set_ylim(240,170) 51 | ax2.set_xticks([]) 52 | ax2.set_yticks([]) 53 | text = ax2.text(0.025, 0.95, 'A', fontsize=32, va="top", weight="bold", 54 | color="white", transform=ax2.transAxes) 55 | text.set_path_effects([path_effects.Stroke(linewidth=2, foreground='black'), 56 | path_effects.Normal()]) 57 | #ax2.set_axis_off() 58 | 59 | # Subplot B 60 | ax3 = plt.axes([0.0, 0.0, 0.5, 0.5]) 61 | ax3.imshow(im, interpolation='none') 62 | ax3.set_xlim(200,300) 63 | ax3.set_ylim( 90, 20) 64 | ax3.set_xticks([]) 65 | ax3.set_yticks([]) 66 | text = ax3.text(0.025, 0.95, 'B', fontsize=32, va="top", weight="bold", 67 | color="white", transform=ax3.transAxes) 68 | text.set_path_effects([path_effects.Stroke(linewidth=2, foreground='black'), 69 | path_effects.Normal()]) 70 | #ax3.set_axis_off() 71 | 72 | # Subplot C 73 | ax4 = plt.axes([0.5, 0.0, 0.5, 0.5]) 74 | ax4.imshow(im, interpolation='none') 75 | ax4.set_xlim(270,370) 76 | ax4.set_ylim(270,200) 77 | ax4.set_xticks([]) 78 | ax4.set_yticks([]) 79 | text = ax4.text(0.025, 0.95, 'C', fontsize=32, va="top", weight="bold", 80 | color="white", transform=ax4.transAxes) 81 | text.set_path_effects([path_effects.Stroke(linewidth=2, foreground='black'), 82 | path_effects.Normal()]) 83 | #ax4.set_axis_off() 84 | 85 | # Show the figure 86 | plt.show() 87 | -------------------------------------------------------------------------------- /exercise-2-sol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 4 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 5 | # ----------------------------------------------------------------------------- 6 | # Scientific visualization course 7 | # G-Node 2016 summer school, University of Reading 8 | # ----------------------------------------------------------------------------- 9 | 10 | # Draw 3 rectangles on the image 11 | convert neurons.jpg \ 12 | -fill none -stroke white -strokewidth 2 -draw "rectangle 100,170,200,240" \ 13 | -fill none -stroke white -strokewidth 2 -draw "rectangle 200, 20,300, 90" \ 14 | -fill none -stroke white -strokewidth 2 -draw "rectangle 270,200,370,270" \ 15 | neurons_abc.jpg 16 | 17 | # Crop, resize and label first subimage (100x70+100+170) 18 | convert neurons.jpg \ 19 | -crop 100x70+100+170 -resize 500x350 \ 20 | -background none -fill white -stroke black -strokewidth 1 \ 21 | -font Helvetica-bold -pointsize 32 label:'(a)' -gravity northwest \ 22 | -geometry +10+12 -composite neurons_a.jpg 23 | 24 | # Crop, resize and label second subimage (100x70+200+20) 25 | convert neurons.jpg \ 26 | -crop 100x70+200+20 -resize 500x350 \ 27 | -background none -fill white -stroke black -strokewidth 1 \ 28 | -font Helvetica-bold -pointsize 32 label:'(b)' -gravity northwest \ 29 | -geometry +10+12 -composite neurons_b.jpg 30 | 31 | # Crop, resize and label third subimage (100x70+270+200) 32 | convert neurons.jpg \ 33 | -crop 100x70+270+200 -resize 500x350 \ 34 | -background none -fill white -stroke black -strokewidth 1 \ 35 | -font Helvetica-bold -pointsize 32 label:'(c)' -gravity northwest \ 36 | -geometry +10+12 -composite neurons_c.jpg 37 | 38 | # Final composition 39 | montage neurons_abc.jpg neurons_a.jpg neurons_b.jpg neurons_c.jpg \ 40 | -tile 2x2 -geometry +3+3 final.jpg 41 | 42 | # Cleanup tmp files 43 | rm neurons_abc.jpg 44 | rm neurons_a.jpg 45 | rm neurons_b.jpg 46 | rm neurons_c.jpg 47 | -------------------------------------------------------------------------------- /exercise-3-sol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/exercise-3-sol.png -------------------------------------------------------------------------------- /exercise-3-sol.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 3 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 4 | # ----------------------------------------------------------------------------- 5 | # Scientific visualization course 6 | # G-Node 2016 summer school, University of Reading 7 | # Inspired by http://merqur.io/2015/10/02/drawing-a-brain-with-bokeh 8 | # ----------------------------------------------------------------------------- 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | X = np.arange(2007,2014) 13 | Y = [75,75,78,79,80,81,82] 14 | 15 | fig = plt.figure(figsize=(10,6)) 16 | ax = fig.add_subplot(111) 17 | 18 | for y in np.arange(0,101,20): 19 | ax.axhline(y,color='.75', zorder=-100) 20 | 21 | plt.scatter(X, Y, s=100, color='white', linewidth=2.5, edgecolor='blue', zorder=10) 22 | plt.plot(X,Y, color='blue', linewidth=2.5, zorder=0) 23 | 24 | plt.xlim(2006,2014) 25 | plt.xticks(X, ['%d' % x for x in X]) 26 | 27 | plt.ylim(0,100) 28 | plt.yticks(np.arange(0,101,20), ["%d%%" % x for x in np.arange(0,101,20)]) 29 | 30 | 31 | ax.spines['right'].set_color('none') 32 | ax.spines['top'].set_color('none') 33 | ax.spines['left'].set_color('none') 34 | ax.xaxis.set_ticks_position('bottom') 35 | ax.yaxis.set_ticks_position('left') 36 | 37 | plt.savefig("exercise-3-sol.png") 38 | plt.show() 39 | -------------------------------------------------------------------------------- /exercise-4-sol.md: -------------------------------------------------------------------------------- 1 | 2 | ### Computing column size 3 | 4 | * DPI = Dots Per Inch 5 | * 1 inch = 2.54cm 6 | * Double-column article on A4 paper (21cm x 29.4cm) 7 | * Margin is 2cm, column separation is 1cm 8 | * One column is ≈(21 - 2x2 - 1)/2 ≈ 8cm 9 | * Figures should be rendered at 300dpi 10 | → 8/2.54*300 ≈ 954 pixels ≈ 1000 pixels wide 11 | 12 | 13 | ### Vector or bitmap 14 | 15 | * If figure has been saved in bitmap format (e.g. PNG), you should ensure it has 16 | sufficient resolution 17 | * If figure has been saved in vector format (e.g. PDF), you don't care about 18 | resolution 19 | 20 | Vector or bitmap ? If you have only a few elements on your figure, vector might 21 | be a good idea. If you have many small elements, bitmap might be a better 22 | option (smaller file size, faster rendering). 23 | -------------------------------------------------------------------------------- /exercise-4-sol.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 3 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 4 | # ----------------------------------------------------------------------------- 5 | # Scientific visualization course (https://github.com/rougier/ASPP-2016) 6 | # Advanced Scientific Python Programming course 7 | # University of Reading, 2016, https://python.g-node.org/wiki/ 8 | # ----------------------------------------------------------------------------- 9 | import matplotlib.pyplot as plt 10 | 11 | dpi = 300 12 | fig = plt.figure(figsize = (3.15,3.15)) 13 | 14 | plt.subplot(111) 15 | plt.savefig("exercise-4.png", dpi=dpi) 16 | plt.show() 17 | 18 | -------------------------------------------------------------------------------- /exercise-5-sol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/exercise-5-sol.png -------------------------------------------------------------------------------- /exercise-5-sol.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (c) 2016, Nicolas P. Rougier. All Rights Reserved. 3 | # Distributed under the (new) BSD License. See LICENSE.txt for more info. 4 | # ----------------------------------------------------------------------------- 5 | # Scientific visualization course 6 | # G-Node 2016 summer school, University of Reading 7 | # Inspired by http://merqur.io/2015/10/02/drawing-a-brain-with-bokeh 8 | # ----------------------------------------------------------------------------- 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | from matplotlib.path import Path 12 | import matplotlib.patches as patches 13 | 14 | n = 75 15 | radius = 1 16 | T = np.linspace(0,2*np.pi,n,endpoint=False) 17 | P = radius * np.vstack((np.cos(T),np.sin(T))).T 18 | C = np.abs(np.random.uniform(0, 1, size=(n,n))) 19 | 20 | # Limit in connection strenght for displaying the link 21 | c_min = 0.99 22 | 23 | # Line width of the weakest and strongest connections 24 | lw_min, lw_max = 0.5, 8.0 25 | 26 | # Colormap to use 27 | cmap = plt.cm.viridis 28 | 29 | # Handy function to draw a cubic Bézier 30 | def bezier(p0, p1, p2, p3, color='k', linewidth=1.0): 31 | codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4] 32 | path = Path(np.array([p0,p1,p2,p3]), codes) 33 | return patches.PathPatch(path, fc='none', 34 | color=color, linewidth=linewidth) 35 | 36 | 37 | # New figure with aspect = 1 38 | fig = plt.figure(figsize=(8,8)) 39 | ax = fig.add_subplot(111, aspect=1) 40 | 41 | # Get connections stronget than c_min 42 | I, J = np.where(C > c_min) 43 | 44 | # Normalized them 45 | vmin, vmax = C[I,J].min(), C[I,J].max() 46 | LW = lw_min + ((C-vmin)/(vmax-vmin))**2 *(lw_max-lw_min) 47 | 48 | # Draw connections 49 | for i,j in zip(I,J): 50 | color = cmap(T[i]/(2*np.pi)) 51 | linewidth = LW[i,j] 52 | # Make connection more visible 53 | # patch = bezier(P[i], P[i]/2, P[j]/2, P[j], color='w', linewidth=linewidth+2) 54 | # ax.add_patch(patch) 55 | patch = bezier(P[i], P[i]/2, P[j]/2, P[j], color=color, linewidth=linewidth) 56 | ax.add_patch(patch) 57 | 58 | # Draw points 59 | plt.scatter(P[:,0],P[:,1], s=100, color=T, linewidth=1.5, 60 | edgecolor='white', cmap=cmap, zorder=10) 61 | 62 | # Draw labels 63 | for i,angle in enumerate(T): 64 | x, y = 1.05*np.cos(angle), 1.05*np.sin(angle) 65 | text = " area %03d" % i 66 | plt.text(x, y, text, size=12, ha="left",va="center", 67 | rotation = 180*angle/np.pi, rotation_mode="anchor") 68 | 69 | plt.xlim(-1.35,+1.35) 70 | plt.ylim(-1.35,+1.35) 71 | plt.xticks([]) 72 | plt.yticks([]) 73 | plt.tight_layout() 74 | plt.savefig("exercise-5-sol.png") 75 | plt.show() 76 | -------------------------------------------------------------------------------- /final.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/final.jpg -------------------------------------------------------------------------------- /neurons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/neurons.jpg -------------------------------------------------------------------------------- /obama.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/obama.jpg -------------------------------------------------------------------------------- /phd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/phd.jpg -------------------------------------------------------------------------------- /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/slides.pdf -------------------------------------------------------------------------------- /trust.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ASPP/ASPP-dataviz-2016/5f7249d0aa0a6e8caea9ef22d5fb8f3bc4703e76/trust.jpg --------------------------------------------------------------------------------