Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
An introduction to scientific programming with Session 5: Extreme Python • Managing your environment • Efficiently handling large datasets • Optimising your code • Squeezing out extra speed • Writing robust code • Graphical interfaces Managing your environment • Some good things about Python • • • ongoing development of Python and modules Some bad things about Python • • • lots of modules from many sources lots of modules from many sources ongoing development of Python and modules A solution • Maintain (or have option to create) separate environments (or manifests) for different projects • virtualenv • • general Python solution – http://virtualenv.pypa.io modules are installed with pip – https://pip.pypa.io $ pip install virtualenv # install virtualenv $ virtualenv ENV1 # create a new environment ENV1 $ source ENV/bin/activate # set PATH to our environment (ENV1)$ pip install emcee # install modules into ENV1 (ENV1)$ pip install numpy==1.8.2 # install specific version (ENV1)$ python # use our custom environment (ENV1)$ deactivate # return our PATH to normal • virtualenv • • can record current state of modules to a 'requirements' file using that file can always recreate the same environment (ENV1)$ pip freeze > requirements.txt $ cat requirements.txt emcee==2.1.0 numpy==1.8.2 $ deactivate $ virtualenv ENV2 $ source ENV2/bin/activate (ENV2)$ pip install -r requirements.txt • conda – http://conda.pydata.org • • • specific to the Anaconda Python distribution similar to 'pip', but can install binaries (not just python) avoid using pip within conda environment (although possible) $ conda create -n ENV1 # create a new environment ENV1 $ source activate ENV1 # set PATH to our environment $ conda install numpy # install modules into ENV1 $ conda install -c thebamf emcee $ source deactivate # install from binstar # return our PATH to normal $ conda list –n ENV1 -e > requirements.txt $ conda create -n ENV2 --file requirements.txt # clone ENV1 # to ENV2 • Updating packages $ conda update --all $ conda update scipy emcee OR $ pip install --upgrade $ pip install --upgrade scipy emcee Efficiently handling large datasets • Python has tools for accessing most (all?) databases • e.g. MySQL, SQLite, MongoDB, Postgres, … • • Allow one to work with huge datasets • However, most databases are designed for webserver use Data can be at remote locations • Not optimised for data analysis • • http://pytables.github.io For creating, storing and analysing datasets • • • • • from simple, small tables to complex, huge datasets standard HDF5 file format incredibly fast – even faster with indexing uses on the fly block compression designed for modern systems • • • fast multi-code CPU; large, slow memory "in-kernel" – data and algorithm are sent to CPU in optimal way "out-of-core" – avoids loading whole dataset into memory • • • • • Can store many things in one HDF5 file (like FITS) Tree structure Everything in a group (starting with root group, '/') Data stored in leaves Arrays (e.g. n-dimensional images) >>> from tables import * >>> h5file = openFile("test.h5", mode = "w") >>> x = h5file.createArray("/", "x", arange(1000)) >>> y = h5file.createArray("/", "y", sqrt(arange(1000))) >>> h5file.close() • Tables (columns with different formats) • • described by a class accessed by a row iterator >>> class MyTable(IsDescription): z = Float32Col() >>> table = h5file.createTable("/", "mytable", MyTable) >>> row = table.row >>> for i in xrange(1000): row["z"] = i**(3.0/2.0) row.append() >>> table.flush() >>> z = table.cols.z • Expr enables in-kernel & out-of-core operations >>> r = h5file.createArray("/", "r", np.zeros(1000)) >>> xyz = Expr("x*y*z") >>> xyz.setOutput(r) >>> xyz.eval() /r (Array(1000,)) '' atom := Float64Atom(shape=(), dflt=0.0) maindim := 0 flavor := 'numpy' byteorder := 'little' chunkshape := None >>> r.read(0, 10) array([ 0. , 64. , 511.99999124, 1. , 7.99999986, 26.9999989 , 124.99999917, 216.00000085, 343.00001259, 729. ]) • where enables in-kernel selections >>> r_bigish = [ row['z'] for row in table.where('(z > 1000) & (z <= 2000)' ] >>> for big in table.where('z > 10000;'): ... print('A big z is {}'.format(big['z']) • There is also a where in Expr • Python Data Analysis Library • Easy-to-use data structures • http://pandas.pydata.org • • • • DataFrame (more friendly recarray) Handles missing data (more friendly masked array) read and write various data formats data-alignment • • Easy to combine data tables Surprisingly fast! • tries to be helpful, though not always intuitive • • 1D Series, 2D DataFrame, 3D Panel Various ways of creating these structures >>> import pandas as pd >>> t = np.arange(0.0, 1.0, 0.1) >>> s = pd.Series(t) >>> x = t + np.random.normal(scale=0.1, size=t.shape) >>> y = x**2 + np.random.normal(scale=0.5, size=t.shape) >>> df1 = pd.DataFrame({ 'x' : x, 'y' : y}, index = t) >>> df1.plot() >>> df1.plot('x', 'y', kind='scatter') • • Always indexed Indices used when joining tables >>> t2 = t[::2] >>> z = t2**3 + np.random.normal(scale=1.0, size=t2.shape) >>> df2 = pd.DataFrame({ 'z' : z, 'z2' : z**2}, index = t2) >>> df3 = df1.join(df2) >>> df3.sort('y') • • • Handling data fairly intuitive, similar to numpy slices Powerful and flexible Good documentation • • DataFrame can be created directly from a recarray Pandas data structures can be efficiently stored on disk • based on PyTables data = pyfits.getdata('myfunkydata.fits') df = pd.DataFrame(data) # store DataFrame to HDF5 store = pd.HDFStore('myfunkydata.h5', mode='w', complib='blosc', complevel=5) store['funkydata'] = df # some time later… store = pd.HDFStore('myfunkydata.h5', mode='r') df = store['funkydata'] Graphical interfaces Several modules to construct GUIs in Python wxpython is one of the most popular http://www.wxpython.org E.g., https://github.com/bamford/control/ Django a high-level Python Web framework that encourages rapid development and clean, pragmatic design. and many others, e.g. Zope (massive), web2py (light), … • Give your scientific code a friendly face! • • • • easy configuration monitor progress particularly for public code, cloud computing, HPC An (unsicentific) example, followed by another one Optimising your code timeit – use in interpreter, script or command line python -m timeit [-n N] [-r N] [-s S] [statement ...] Options: -s S, --setup=S statement to be executed once initially (default pass) -n N, --number=N how many times to execute 'statement' (default: take ~0.2 sec total) -r N, --repeat=N how many times to repeat the timer (default 3) iPython magic version %timeit %%timeit # one line # whole notebook cell # fastest way to calculate x**5? $ python -m timeit -s 'from math import pow; x = 1.23' 'x*x*x*x*x' 10000000 loops, best of 3: 0.161 usec per loop $ python -m timeit -s 'from math import pow; x = 1.23' 'x**5' 10000000 loops, best of 3: 0.111 usec per loop $ python -m timeit -s 'from math import pow; x = 1.23' 'pow(x, 5)' 1000000 loops, best of 3: 0.184 usec per loop • Understand which parts of your code limit its execution time • print summary to screen, or save file for detailed analysis From shell $ python -m cProfile –o program.prof my_program.py From iPython %prun -D program.prof my_function() %%prun # profile an entire notebook cell Lots of functionality… see docs Nice visualisation with snakeviz – http://jiffyclub.github.io/snakeviz/ $ conda install –c thebamf snakeviz OR $ pip install snakeviz In iPython: %load_ext snakeviz %snakeviz my_function() %%snakeviz # profile entire cell Writing robust code • Several testing frameworks • • unittest is the main Python module nose is a third-party module that nicely automates testing Squeezing out extra speed • Python includes modules for writing "parallel" programs: • • threaded – limited by the Global Interpreter Lock multiprocessing – generally more useful from multiprocessing import Pool def f(x): return x*x pool = Pool(processes=4) z = range(10) print pool.map(f, z) # start 4 worker processes # apply f to each element of z in parallel from multiprocessing import Process from time import sleep def f(name): print('Hello {}, I am going to sleep now'.format(name)) sleep(3) print('OK, finished sleeping') if __name__ == '__main__': p = Process(target=f, args=(lock, 'Steven')) p.start() # start additional process sleep(1) # carry on doing stuff print 'Wow, how lazy is that function!' p.join() # wait for process to complete $ python thinking.py Hello Steven, I am going to sleep now Wow, how lazy is that function! OK, finished sleeping (Really, should use a lock to avoid writing output to screen at same time) Cython is used for compiling Python-like code to machine-code • • • • • Cython code is turned into C code • • supports a big subset of the Python language conditions and loops run 2-8x faster, overall 30% faster for plain Python code add types for speedups (hundreds of times) easily use native libraries (C/C++/Fortran) directly uses the CPython API and runtime Coding in Cython is like coding in Python and C at the same time! Some material borrowed from Dag Sverre Seljebotn (University of Oslo) EuroSciPy 2010 presentation Use cases: • Performance-critical code • • • which does not translate to array-based approach (numpy / pytables) existing Python code ! optimise critical parts Wrapping existing C/C++ libraries • • particularly higher-level Pythonised wrapper for one-to-one wrapping other tools might be better suited Cython code must be compiled. Two stages: • A .pyx file is compiled by Cython to a .c file, containing the code of a Python extension module • The .c file is compiled by a C compiler • • • • Generated C code can be built without Cython installed Cython is a developer dependency, not a build-time dependency Generated C code works with Python 2.3+ The result is a .so file (or .pyd on Windows) which can be imported directly into a Python session Ways of building Cython code: • Run cython command-line utility and compile the resulting C file • • • Use pyximport to importing Cython .pyx files as if they were .py files; building on the fly (recommended to start). • • • use favourite build tool for cross-system operation you need to query Python for the C build options to use things get complicated if you must link to native libraries larger projects tend to need a build phase anyway Write a distutils setup.py • standard way of distributing, building and installing Python modules • • Cython supports most of normal Python Most standard Python code can be used directly with Cython • • • typical speedups of (very roughly) a factor of two should not ever slow down code – safe to try name file .pyx or use pyimport = True >>> import pyximport >>> pyximport.install() >>> import mypyxmodule # converts and compiles on the fly >>> pyximport.install(pyimport = True) >>> import mypymodule # converts and compiles on the fly # should fall back to Python if fails • • • • • Big speedup from defining types of key variables Use native C-types (int, double, char *, etc.) Use Python C-types (Py_int_t, Py_float_t, etc.) Use cdef to declare variable types Also use cdef to declare C-only functions (with return type) • • can also use cpdef to declare functions which are automatically treated as C or Python depending on usage Don't forget function arguments (but note cdef not used here) • Efficient algorithm to find first N prime numbers def primes(kmax): p = [] k = 0 n = 2 while k < kmax: i = 0 while i < k and n % p[i] != 0: i = i + 1 if i == k: k = k + 1 p.append(n) n = n + 1 return p primes.py $ python -m timeit -s 'import primes as p' 'p.primes(100)' 1000 loops, best of 3: 1.35 msec per loop cprimes.pyx def primes(kmax): p = [] k = 0 n = 2 while k < kmax: i = 0 while i < k and n % p[i] != 0: i = i + 1 if i == k: k = k + 1 p.append(n) n = n + 1 return p $ python -m timeit -s 'import pyximport; pyximport.install(); import cprimes as p' 'p.primes(100)' 1000 loops, best of 3: 731 usec per loop 1.8x speedup xprimes.pyx def primes(int kmax): # declare types of parameters cdef int n, k, i # declare types of variables cdef int p[1000] # including arrays result = [] # can still use normal Python types if kmax > 1000: # in this case need to hardcode limit kmax = 1000 k = 0 n = 2 while k < kmax: i = 0 while i < k and n % p[i] != 0: contains only C-code i = i + 1 if i == k: p[k] = n 40.8 usec per loop k = k + 1 33x speedup result.append(n) n = n + 1 return result # return Python object • Cython provides a way to quickly access Numpy arrays with specified types and dimensionality ! for implementing fast specific algorithms • Also provides way to create generalized Ufuncs • Can be useful, but often using functions provided by numpy, scipy, numexpr or pytables will be easier and faster • • JIT: just in time compilation of Python functions Compilation for both CPU and GPU hardware from numba import jit @jit def sum2d(arr): M, N = arr.shape result = 0.0 for i in range(M): for j in range(N): result += arr[i,j] return result a = np.arange(10000).reshape(1000,10) %timeit sum2d(a) 1000 loops, best of 3: 334 µs per loop %timeit sum2d(a) # without @jit 1 loops, best of 3: 2.15 µs per loop An introduction to scientific programming with The End