Python For Everyone: Advanced Course by @jeremy_carson Presentation developed with Remark

1 / 112

Details This presentation was developed with Remark, a MarkDown presentation API. Course materials Python Advanced Course [HTML] [PDF] Raw Markdown Data [course data] Source code for presentation and any examples [link] License Unless otherwise specified, all Python Presentation source code is released under the MIT license. This tutorial uses examples from several sources, I tried to provide attribution where applicable. Many online tutorials I referenced do not include an explicit license so I attributed it to their website. The MIT License (MIT) Copyright (c) 2014 Jeremy Carson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

2 / 112

Advanced Fundamentals

3 / 112

Regular Expressions Regular expressions (regexes, REs, regex patterns) are algebraic ways of describing natural language. Features: Incredibly useful for parsing semi­structured data Pattern Matching/Searching (e.g. Exact match vs Partial match) String Splitting Substring Extraction Semi­structured data: name,phone jenny,867‐5309 other,867‐5308 jason,1234567 mike,(978) 407‐1866

Row = (Name,PhoneNumber) Phone numbers are not well formatted (but are good enough for REs) No str.find() method is smart enough extract these data. Requires pattern matching. Regular Expressions are a language unto themselves, read up: Official Python Regex Tutorial [link] Official Python Regex Documentation [link] Good Regular Expression Site [link]

4 / 112

Regular Expressions

5 / 112

Regular Expressions Example: \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b

Regex meta­characters: . ^ $ * + ? { } [ ] \ | ( )

These are not definitive. Several meta­characters even have different meanings, depending on context. See the [official docs] for more [] ­ Match anything inside the square brackets for ONE character position. A ‐ inside brackets indicates a range separator. [abcdefg] could be written as [a‐g] A ^ inside brackets indicates a negation. [^abc] match any characaters not a,b,c. \ ­ Escape character with special meaning. e.g. \d is equivalent to [0‐9] You can also use the backslash to remove special meanings. e.g. finding square brackets: [\[\]] Special sequences can be included in character classes. e.g. [\s,.] matches any whitespace, comma, and period. * ­ Not a wildcard, means that the previous character can be matched 0 or more times. e.g. ca*t matches "ct","cat","caaaaaaaat" + ­ Previous character can be matched 1 or more times.

6 / 112

Regular Expressions Let's go back to the phone numbers: data =[ "jenny,867‐5309", "other,867‐5308", "jason,1234567", "mike,(978) 407‐1866"]

Attempt 1 import re p = re.compile(r"\d+‐\d+") for phone in data:     result = p.findall(phone)     if result:         print(result)

re.compile() ­ Create a new regular expression program using some regex string Lead regex strings w/ r to avoid problems parsing \ r"\test" is equivalent to r"\\test" Important for regex, \d is to be evaluated by regex, not Pythjon. findall() ­ Returns all substrings of  that match the regex

7 / 112

Regular Expressions Attempt 2 p = re.compile(r"\d+‐*\d+") for phone in data:     result = p.findall(phone)     if result:         print(result)

Better but doesn't get last phone number. Attempt 3 (right off the slide???):

(?:(?:\+?1\s*(?:[.‐]\s*)?)?(?:\(\s*([2‐9]1[02‐9]|[2‐9][02‐8]1|[2‐9][02‐8][02‐9])\s*\)|([2‐9]1[02‐9]|[2‐9][02‐

Found this online, I hope the user was just trolling cause this is awful. Attempt 4 (work smarter): p = re.compile(r"(?:\d{10}|\d{7})") for phone in data:     phone = re.sub(r"[()\‐ ]",'',phone)     result = p.findall(phone)     if result:         print(result)

Why not remove all the whitespace and parens and just look for sequences of numbers. re.compile(r"(?:\d{10}|\d{7})") ­ Search for 10 numbers in a row or 7 numbers in a row. re.sub(r"[()\‐ ]",'',phone) ­ Remove whitespace, parens, and dashes. This was all our phone numbers become just strings of digits.

8 / 112

Regular Expressions match() ­ Determine if the RE matches at the beginning of the string. search() ­ Scan through a string, looking for any location where this RE matches. findall() ­ Find all substrings where the RE matches, and returns them as a list. finditer() ­ Find all substrings where the RE matches, and returns them as an iterator. match and search return None if no match is found. findall pulls all substring matches: import re p = re.compile("[a‐z]{3}") # match 3 letters result = p.findall("abcdefghi") print(result) # prints ['abc','def','geh']

finditer pulls all substring match objects: p = re.compile("[a‐z]{3}") # match 3 letters result = p.finditer("abcdefghi") print([i.group() for i in result])

group() always returns the match Note: result is an iterator, once used it cannot be used again.

9 / 112

Regular Expressions Up until now we've just been matching strings, ( ) can be used to extract substrings from matches: # Match groups of 3 letters, assume there are 3 groups. p = re.compile(r"(?P[a‐z]{3})([a‐z]{3})([a‐z]{3})") result_iterator = p.finditer("abcdefghi") for result in result_iterator:     print(result.group(0)) # abcdefgeh ‐ always the full match     print(result.group(1)) # abc     print(result.group(2)) # def     print(result.group(3)) # ghi     print(result.group("id")) # abc

There are lots more ways of doing groups. Groups with ? at the beginning are treated differently (?P) ­ Creates an identifier group, allows result.group("id") (?:[abc]|) ­ Creates a non­capturing group, useful if you are using groups and want to avoid parsing unnecessary data. Regular expressions are a completely new language and require time and practice. The best advice I can give is when creating a regex, work through your desired comparison left to right. Start by matching pieces first, then the whole.

10 / 112

Lambdas My favoritest thing in any language. Lambda ­ An unnamed functions useful for one off tasks. Lambdas are syntactic sugar, not necessary but pretty. Lambdas implicitly return a value. Standard function: from functools import reduce x = [i for i in range(1,10)] def mult(a,b):     return a*b # Return the result of multipling all numbers in x reduce(mult,x)

Lambda equivalent: from functools import reduce x = [i for i in range(1,10)] reduce(lambda a,b: a*b,x)

Clean, self contained Lambdas are only be a single line. a and b are the parameters for the lambda There can be 0­>infinite lambda arguments, it depends on how you call the lambda reduce() always passes 2 arguments.

11 / 112

Lambdas Closures are another part of lambdas. Closure is just a clever way of saying your lambda can see variables in the enclosing scope. def multby(value):     return lambda a:a*value multby3 = multby(3) print([multby3(i) for i in range(0,4)]) # 0,3,6,9

value is part of the enclosing scope Notice that I'm returning the lambda to multby3 Now I can dynamically define multiply­by behaviour One gotcha. Python is late­binding. def create_multipliers():     return [lambda x : i * x for i in range(5)] for m in create_multipliers():     print(m(2))

What gets printed?

12 / 112

Lambdas Closures are another part of lambdas. Closure is just a clever way of saying your lambda can see variables in the enclosing scope. def multby(value):     return lambda a:a*value multby3 = multby(3) print([multby3(i) for i in range(0,4)]) # 0,3,6,9

value is part of the enclosing scope Notice that I'm returning the lambda to multby3 Now I can dynamically define multiply­by behaviour One gotcha. Python is late­binding. def create_multipliers():     return [lambda x : i * x for i in range(5)] for m in create_multipliers():     print(m(2))

What gets printed? 8,8,8,8 This is due to late binding. i is referenced by the lambda in create_multipliers but Python waits to define i until it's needed. The first time the lambda is called, i is already storing 4.

13 / 112

Lambdas Always explicitly pass enclosing variables to your lambdas as arguments This forces Python to bind immediately with the current value Remember that default parameter must come after non­defult positional parameters def create_multipliers():     return [lambda x,a=i : a * x for i in range(5)] for m in create_multipliers():     print(m(2))

Does this print what we want?

14 / 112

Lambdas Always explicitly pass enclosing variables to your lambdas as arguments This forces Python to bind immediately with the current value Remember that default parameter must come after non­defult positional parameters def create_multipliers():     return [lambda x,a=i : a * x for i in range(5)] for m in create_multipliers():     print(m(2))

Does this print what we want? Yes: 0,2,4,6,8

15 / 112

Threading I'm going to keep this brief, threading is unlikely to be useful to anyone other than a software engineer. Excerpt from thread_example.py: import threading write_lock = threading.Lock() total = 0 def sum_worker(x,start,stride):     index = start     while index  4.5) go here or here Prefer PyQt4 to PyQt5, PyQt4 still more common Additional Installation ­ Qt Designer We'll focus on writing GUIs in code but you can also make them visually Qt Designer comes with the official PyQt download Qt Designer also comes with the official Qt download For PyQt4, get Qt version 4.8 Foy PYQt5, get Qt version 5.X Validate the installation: from PyQt4.QtCore import QT_VERSION_STR print(QT_VERSION_STR) # should print something like 4.8.6

56 / 112

PyQt ­ What is Qt? Qt is a cross­platform framework for developing user interfaces. Developed by Trolltech, maintained by Nokia. Written in C++ Supports Windows, OS X, Linux, and Unix. Available under GNU Public License (GPL) Available under a commercial license as well (costs $$$) GPL: If you sell or distribute your code you must include the GPL license and make your code open‐source. Code distributed internally to other NSRDEC employees only needs to be shared with those employees.

Commercial License Do what you like, no need to share.

57 / 112

PyQt ­ Features Qt was written to extend all of C++, not just GUIs Supports boat loads of: widgets, layouts, styles, fonts, colors, prairie dogs, polar bears, yak (yakses, yakis... yaksercise?) Supports standard UI features like: menus, status bars, toolbars, drag and drop Simplified communication layer based on signals and slots (PyQt4.5 and later have a custom signal/slot language) Unified Painting System Intelligent style hierarchy, changes to parent style affect children (or not, your choice) Others Networking Threading Videos XML handlers All sorts of community support

58 / 112

PyQt ­ Structure

59 / 112

PyQt ­ Widgets Everything you put in to a Qt user interface is a widget

60 / 112

PyQt ­ Look and Feel Styles provide a way to control the look and feel of a GUI. These can be used to ensure your application has a native look for your operating system. We won't be covering these, the default is sufficient.

Layouts are another feature for creating aesthetically pleasing widgets. Child widgets report their needs to their parents and the parent handles final arrangement. Useful if you want to be able to resize your window in real­time.

61 / 112

PyQt ­ The basics Creating a window(parentless widget): app = QtGui.QApplication(sys.argv) window = QtGui.QWidget() window.resize(600, 600) window.show() sys.exit(app.exec_())

Create an application (always required) Create a parentless widget (window) Resize by (width,height) and show Exit to console when app done Create a child widget (buttons, lists, textboxes, etc): button = QtGui.QPushButton("Quit", window) button.move(200, 200) button.setToolTip("I'll quit")

Create a push button (responds to mouse) Position button and add hover text Extend QWidget: class Example(QtGui.QWidget)     def __init__(self):         super(Example, self).__init__()     def initUI(self):         pass

62 / 112

PyQt ­ Layout basics Layouts manage how child widgets are aligned. You can add widgets any where you like, as you saw on the last slide, but this requires a lot of manual tweaking. Layouts are responsible for: Updating child widget size and position Providing default and minimum child widget size.

63 / 112

PyQt ­ Layout basics

Applying horizontal and vertical layouts to your widgets: ok_button = QtGui.QPushButton("OK") cancel_button = QtGui.QPushButton("Cancel") hbox = QtGui.QHBoxLayout() hbox.addStretch(1) hbox.addWidget(ok_button) hbox.addWidget(cancel_button) vbox = QtGui.QVBoxLayout() vbox.addStretch(1) vbox.addLayout(hbox) window.setLayout(vbox)

Create two buttons and align them using a horizontal layout (purple) Pack the two buttons on the bottom of the dialog using a vertical layout (blue) Stretch values create empty space that expand and contracts (green)

64 / 112

PyQt ­ Signals and Slots

Internally, Qt is powered by an event loop (awesome image above) 1.  User interacts with interface 2.  Interface sends a message to event queue 3.  Event loop processes queue 4.  Qt keeps working without freezing To interact with widgets you need use signals and slots Signal ­ Message sender(widget) Slot ­ Message receiver(widget) Connection ­ Interface between signals and slots

65 / 112

PyQt ­ Signals and Slots Let's revisit our button example but now we'll do something: quit_button = QtGui.QPushButton("&Quit") # Connect: PyQt Version = 4.5 quit_button.clicked.connect( QtGui.qApp.quit) vbox = QtGui.QVBoxLayout() vbox.addStretch(1) vbox.addWidget(quit_button) window.setLayout(vbox)

Create a quit button & is a shortcut that underlines the next letter and creates an alt+ shortcut. Connect the quit button's click signal to qApp.quit Program exists when button is clicked or alt+q is pressed For sanity sake, use the new­style 4.5 connect api

66 / 112

PyQt ­ Premade Dialogs

Qt comes complete with several common dialog boxes pre­built: QColorDialog ­ Standard color selection dialog box QFileDialog ­ Standard file(Open/Save) selection dialog box QFontDialog ­ Standard font selection dialog box Quick reference:     # Open file     file_name = QtGui.QFileDialog.getOpenFileName(window,'Open file',r"C:\StartFolder")     # Select a font     font, ok = QtGui.QFontDialog.getFont()     # Select a color     color = QtGui.QColorDialog.getColor()

67 / 112

PyQt ­ Main Window Qt comes with a standard main application window that enables a skeleton framework upon which to build your application. Features like statusbar, toolbar, and menubar are included. class Example(QtGui.QMainWindow):     def __init__(self):         super(Example, self).__init__()         self.initUI()     def initUI(self):         textEdit = QtGui.QTextEdit()         self.setCentralWidget(textEdit)         exitAction = \             QtGui.QAction(QtGui.QIcon(\             './media/exit.png'), 'Exit', self)         exitAction.setShortcut('Ctrl+Q')         exitAction.triggered.connect(self.close)         self.statusBar()         menubar = self.menuBar()         fileMenu = menubar.addMenu('&File')         fileMenu.addAction(exitAction)         toolbar = self.addToolBar('Exit')         toolbar.addAction(exitAction)         self.setGeometry(300, 300, 350, 250)         self.show()

68 / 112 Extend QMainWindow

PyQt ­ Putting it all together

Complete example at /pyqt/pyqt_full_example.py Demonstrates several features: QMainWindow Menu, Status, and Tool bars. Signals, Slots, and Actions Standard File, Color, and Font dialogs Loading CSV data in to a QTableWidget Additional Resources: Zetcode PyQt4 tutorial [link] Trolltech PyQt4 tutorial [link]

69 / 112

Scientific Python

70 / 112

Numpy vs. Scipy

Numpy and Scipy form the backbone of scientific computing in Python. Numpy ­ High performance multidimensional array functions Scipy ­ Numerical analysis code Numpy and Scipy overlap Numpy was partially built in to Scipy Numpy retains its numerical analysis roots for backwards compatibility ! Prefer Numpy for all array functionality ! Prefer Scipy for all numerical code In an ideal world, NumPy would contain nothing but the array data type and the most basic operations: indexing, sorting, reshaping, basic elementwise functions, et cetera. All numerical code would reside in SciPy. ­ Scipy docs 71 / 112

Numpy Numpy is the fundamental multidimensional array module. It is used by countless other modules and by all of Scipy. Consider it your array of choice for all things scientific. Some key features include: N­Dimensional Array Objects Powerfully fast arrays (much better than list) Fourier Transforms Linear Algebra Functions Advanced randomization capabilities Limitations: Numpy arrays are homogeneous (no mixing numbers and strings) Numpy arrays are fixed size (defined at creation) Append operations return new arrays Numpy bad at non­numerical data Numpy arrays can be slower than lists, always prefer vectorized numpy algorithms over hand­rolling your own code.

72 / 112

Numpy

First some terms: axes ­ The dimensions of your array. rank(ndim) ­ Number of axes(dimensions) in the array shape ­ The size of the array along each dimension. An NxM matrix would have a shape tuple (n,m) Therefore length of shape tuple is the rank (ndim) size ­ Total number of elements in array. N*M dtype ­ Data type, refers to the data in array 73 / 112

Numpy Example: import numpy as np a = np.arange(9).reshape(3,3) print(a.shape) # (3,3) print(a.ndim)  # 2 print(a.size)  # 9 print(a.dtype) # int32

Create an array of 9 elements Reshape the array to 3x3 matrix Print some array information arange is Numpy's range equivalent Array Creation: a = np.array( [1,2,3,4] ) print(a)        # [1,2,3,4] print(a.dtype)  # int32 a = np.array( [1.,2.,3.,4.] ) print(a)       # [1.,2.,3.,4.] print(a.dtype) # float64 # WRONG # a = np.array(1,2,3,4) # 2D Array a = np.array([ [1,2], [3,4]]) print(a)

74 / 112

Numpy Generating Filled Arrays: a = np.zeros([3,3]) print(a) a = np.ones([3,3],dtype=np.float64) print(a) a = np.empty([3,3]) print(a)

Create a 3x3 array of zeroes Create a 3x# array of ones data type can be specified Create an empty array values default to junk data Generating Filled Arrays with ranges: a = np.arange(0,10,2) print(a) a = np.arange(0,2,0.3) print(a) a = np.linspace( 0, 2, 9 ) print(a) a = linspace( 0, 2*np.pi, 10np. ) f = np.sin(x) print(f)

75 / 112

Numpy Printings Arrays: a = np.arange(12) print(a) a = np.arange(12).reshape(3,4) print(a) a = np.arange(24).reshape(2,3,4) print(a) a = np.arange(10000).reshape(100,100) print(a)

Create some arrays reshape(values) reshapes the array to match values length(values) = ndim values = shape Multiplicative sum of values must equal original array size. Print some arrays Numpy skips printing central part of large array To disable this feature: np.set_printoptions(threshold = 'nan') Basic arithmetic operations: a = np.arange(4) b = np.arange(4) c = a‐b print(c) c = a+b

76 / 112

Numpy Basic scalar operations: c = a**2 print(c) c = a+1 print(c) print(a > 2) c = a*2 print(c)

Unlike Python lists you can operate on Numpy arrays with scalars Broadcasting is Numpy's technique for dealing with mismatched array shapes. [docs] [1,2,3] * [1] becomes [1,2,3] * [1,1,1] Matrix arithmetic: a = np.array([     [1,1],     [0,1] ]) b = np.array([     [2,0],     [3,4] ]) c = a*b # elementwise product c = np.dot(a,b) # matrix product

Multiplications are elementwise Numpy's dot function will perform a matrix product

77 / 112

Numpy Unary operations a = np.array([[1,2,3,4],[5,6,7,8]]) print(a.min()) print(a.max()) print(a.sum()) print(a.cumsum()) print(a.cumsum(axis=0)) print(a.cumsum(axis=1)) print(a.transpose()) print(a.inverse())

Calculate the min and max of a matrix Calculate the sum of a matrix Calculate the cumulative sum of a matrix No axis defined means flatten 2D array to 1D Axis 0 is the first dimension, column cumulative sum. [1,2,3,4] + [5,6,7,8] Axis 1 is the second dimension, row cumulative sum. [1,1+2,1+2+3,1+2+3+4] Universal operations: a = np.array([10,9,8,7,6,5]) print(np.exp(a)) print(np.sqrt(a)) print(np.sort(a))

Create an array Calculate exponential of each element Calculate square root of each element

78 / 112

Numpy 1D Indexing, Slicing and Iterating: a = np.array([1,2,3,4,5]) print(a[0])     # first element print(a[‐1])    # last element print(a[2:5])   # a[2]...a[5] print(a[::‐1])  # Reversed array for elem in a:     print(elem)

Numpy arrays support the same Indexing, Slicing and Iterating ops as lists Remember: Python is base­0 indexing. Iterate over each element ND Indexing, Slicing: a = np.arange(1,10).reshape(3,3) #2D print(a[0][0]) # Old style list indexing print(a[0,0])  # Numpy indexing print(a[1:,1:]) # remove first row, first col print(a[:,1:]) # remove first col print(a[:,...]) # remove first col for elem in a:     print(elem)

Numpy extends Python list slicing with the ability to select the slice axis Commas separate dimensions a[1:,1:]

79 / 112

Numpy Iterating Hack: a = np.arange(1,10).reshape(3,3) #2D for i in range(c.shape[‐1]):     print c[...,i]

Iterate over the last dimension, in this case it gives us columns

80 / 112

Numpy Stacking: a = np.array([[1,2,3],[4,5,6]]) b = np.array([[7,8,9],[10,11,12]]) print(np.vstack((a,b))) print(np.hstack((a,b)))

Arrays can be stacked horizontally or vertically Copying: a = np.array([[1,2,3],[4,5,6]]) b = a b[0] = 12 print(a) print(b) a = np.array([[1,2,3],[4,5,6]]) b = a.copy() b[0] = 12 print(a) print(b)

Assigning new variable names to the same array does not create a copy Deep copies must be explicitly called with array.copy

81 / 112

Numpy for Matlab users Matlab NumPy a = [1 2 3; 4 5 6] a = array([[1.,2.,3.],[4.,5.,6.]]) a(end) a[­1] a(2,5) a[1,4] a(2,:) a[1] or a[1,:] a(1:5,:) a[0:5] or a[:5] or a[0:5,:] a(end­4:end,:) a[­5:] a(1:3,5:9) a[0:3][:,4:9] a(1:2:end,:) a[::2,:] a(end:­1:1,:) or flipud(a) a[::­1,:] a.’ a.transpose() or a.T a’ a.conj().transpose() or a.conj().T a * b dot(a,b) a .* b a * b a./b a/b Source

82 / 112

Numpy for Matlab users Matlab

NumPy

a.∧3

a**3

find(a>0.5) where(a>0.5) a(aDilation a = np.zeros((5,5), dtype=np.int) a[1:4, 1:4] = 1; a[4, 4] = 1 print(a) b=ndimage.binary_opening(a, structure=np.ones((3,3))).astype(np.int) print(b) b=ndimage.binary_opening(a).astype(np.int) print(b) # Closing = Dilation‐>Erosion a = np.zeros((5,5), dtype=np.int) a[1:4, 1:2] = 1; #a[4, 4] = 1 print(a) b=ndimage.binary_closing(a, structure=np.ones((3,3))).astype(np.int) print(b) b=ndimage.binary_closing(a).astype(np.int) print(b)

106 / 112

Scipy: Image processing Lastly I want to quickly show how detect edges from scipy import ndimage import matplotlib.pyplot as plt im = np.zeros((256, 256)) im[64:‐64, 64:‐64] = 1 im = ndimage.rotate(im, 15, mode='constant') im = ndimage.gaussian_filter(im, 8) sx = ndimage.sobel(im, axis=0, \  mode='constant') sy = ndimage.sobel(im, axis=1, \  mode='constant') sxsy = np.hypot(sx,sy) plt.imshow(sxsy) plt.show()

Create some dummy data Rotate that data to give it some pizazz Blur a little to make edge detection more pronounced Apply sobel filter Sobel is an edge detection algorithm

107 / 112

Scipy: Statistics SciPy statistics is the last Scipy module we'll cover. This has barely scratched the surface of what SciPy can do and I would suggest looking at the SciPy site for more information. Histograms and probabilities: from scipy import stats import numpy as np import pylab as pl a = np.random.normal(size=10000) bins = np.linspace(‐5, 5, 30) histogram, bins = np.histogram(a,\  bins=bins, normed=True) bins = 0.5*(bins[1:] + bins[:‐1])

from scipy import stats b = stats.norm.pdf(bins) pl.plot(bins, histogram) pl.plot(bins, b) pl.show() print(stats.ttest_ind(bins, b) )

Generate statistically random data Important: Python's built­in random module isn't rigorous. Hack method for quick and dirty calculations Bin the random data in a histogram Bins range from [­5,5] Finally create a probability distribution from the bin range Shows how data should be distributed

108 / 112

Scipy: Statistics Percentiles: from scipy import stats import numpy as np import pylab as pl a = np.random.normal(size=10000) b = np.median(a) c = stats.scoreatpercentile(a, 50)   d = stats.scoreatpercentile(a, 90) print(a) print(b) print(c) print(d)

Median is the value w/ half of observations fall below it Generate a 50th percentile and a 90th percentile 50th percentile should match median value Percentiles refer to values for which N% of observations fall below it

109 / 112

Scipy: Practical Usage import sys import os from numpy import corrcoef, sum, log, arange from pylab import pcolor, show from pylab import colorbar, xticks, yticks from scipy.signal import correlate2d as corrcoef2 def load_csv(path):     data = []     with open(path,"r") as file:         headers = file.next().split(',')         for line in file:             tokens = line.strip().split(',')             datum = [float(a) \                 if len(a)!=0  \                 else 0 for a in tokens]             data.append(datum)             assert len(datum) == len(headers)     return [data,headers] def transpose(data):     return zip(*data) data,headers = load_csv("./data/msrdata.csv") oriented_data = transpose(data); R = corrcoef(oriented_data) pcolor(R,cmap="RdYlGn") colorbar() yticks(arange(0.5,len(headers)),headers) xticks(arange(0.5,len(headers)),headers) show()

110 / 112

Scipy: Practical Usage

My hopes were dashed pretty quickly. 111 / 112

Fin.

112 / 112