├── .gitignore ├── Lecture 8 ├── test.sh ├── L8_example_subprocess.py └── L8_lecture.py ├── Lecture 5 ├── Homework 1 │ ├── ulam_spiral.png │ ├── ulam_spiral_500.png │ └── L5_homework1_text.txt ├── Homework 2 │ ├── nearest stars.png │ ├── star_data_documentation.txt │ ├── star_data.txt │ ├── TextFileParser.py │ └── L5_homework2_text.txt ├── L5_example_binary_files.py └── L5_lecture.py ├── Lecture 4 ├── Homework 1 │ ├── L4_homework1_result.png │ ├── L4_homework1_generate_data.py │ └── L4_homework1_text.txt ├── Homework 2 │ └── L4_homework2_text.txt ├── Task 1 │ ├── TextFileParser.py │ └── data.txt └── L4_lecture.py ├── Lecture 2 ├── L2_example1.py ├── Task 1 │ └── L2_T1_text.txt ├── data.txt └── L2_lecture.py ├── Lecture 7 ├── misc │ ├── make_spline_data.py │ ├── conv_radiometer.py │ └── make_radiometer_data.py ├── TextFileParser.py ├── spline_data.csv └── L7_lecture.py ├── Lecture 3 ├── Homework │ ├── L3_homework_generate_data.py │ └── L3_homework_text.txt ├── data.txt ├── Task 1 │ ├── L3_T1_text.txt │ └── L3_T1_generate_data.py └── L3_lecture.py ├── Lecture 6 ├── Homework 1 │ ├── conv.py │ ├── TextFileParser.py │ └── L6_homework_text.txt ├── Task 1 │ ├── TextFileParser.py │ ├── meteor_data.txt │ └── L6_T1_solution.py └── L6_lecture.py ├── LICENSE ├── ParseTextFile.py ├── Lecture 10 ├── AverageImage.pyx ├── L10_lecture_cython.py ├── L10_lecture.py └── AverageImage.html ├── Lecture 9 ├── PyDomainParallelizer.py └── L9_lecture.py ├── Lecture 1 └── L1_lecture.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Python compiled files 2 | *.pyc -------------------------------------------------------------------------------- /Lecture 8/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "test" -------------------------------------------------------------------------------- /Lecture 5/Homework 1/ulam_spiral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvida/UWO-PA-Python-Course/HEAD/Lecture 5/Homework 1/ulam_spiral.png -------------------------------------------------------------------------------- /Lecture 5/Homework 2/nearest stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvida/UWO-PA-Python-Course/HEAD/Lecture 5/Homework 2/nearest stars.png -------------------------------------------------------------------------------- /Lecture 5/Homework 1/ulam_spiral_500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvida/UWO-PA-Python-Course/HEAD/Lecture 5/Homework 1/ulam_spiral_500.png -------------------------------------------------------------------------------- /Lecture 4/Homework 1/L4_homework1_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvida/UWO-PA-Python-Course/HEAD/Lecture 4/Homework 1/L4_homework1_result.png -------------------------------------------------------------------------------- /Lecture 4/Homework 2/L4_homework2_text.txt: -------------------------------------------------------------------------------- 1 | Make a function that will tell you how many seconds you have been alive (hint: take a look at the datetime library), the function will take a string as an argument, the date and time of your birth in the “YYYY-mm-dd HH:MM:SS” format. I am giving you a complete artistic freedom on how you should actually approach this problem. -------------------------------------------------------------------------------- /Lecture 2/L2_example1.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | """ 4 | Converting decimal numbers to binary. 5 | """ 6 | 7 | # Our initial number 8 | num = 9 9 | 10 | # Init the binary number list 11 | num_bin = [] 12 | 13 | while True: 14 | 15 | # Calculate the remainder 16 | rem = num%2 17 | 18 | # Add the remainder to the number list 19 | num_bin.append(str(rem)) 20 | 21 | # Divide the number by 2 (integer division) 22 | num = int(num)//2 23 | 24 | # Break the loop if the number is 0 25 | if num == 0: 26 | break 27 | 28 | # Reverse the list 29 | num_bin = reversed(num_bin) 30 | 31 | print(''.join(num_bin)) -------------------------------------------------------------------------------- /Lecture 7/misc/make_spline_data.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | file_name = 'spline_data.csv' 6 | 7 | 8 | 9 | def func(x): 10 | 11 | freq = 1.4 12 | 13 | return 10*np.sin(freq*x + 0.457) + 5*np.sin(3*freq*x + 0.247) + 2.5*np.sin(6*freq*x + 0.247) + 3*np.random.random(size=len(x)) 14 | 15 | 16 | # Generate X range 17 | x = np.linspace(11.875, 16.5478, 80) 18 | 19 | 20 | y = func(x) 21 | 22 | plt.scatter(x, y) 23 | plt.show() 24 | 25 | 26 | with open(file_name, 'w') as f: 27 | for row in zip(x, y): 28 | f.write('{:.6f},{:.6f}\n'.format(row[0], row[1])) 29 | 30 | -------------------------------------------------------------------------------- /Lecture 7/misc/conv_radiometer.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | 3 | raw_data_file = '2016-03-19_04.09.03.794.csv' 4 | filtered_data_file = 'radiometer_data.csv' 5 | 6 | 7 | with open(raw_data_file) as f: 8 | 9 | with open(filtered_data_file, 'w') as f2: 10 | 11 | first_line = True 12 | for line in f: 13 | 14 | line = line.split(',') 15 | 16 | date = datetime.strptime(line[0], '%Y%m%d-%H%M%S.%f') 17 | 18 | if first_line: 19 | first_timestamp = date 20 | first_line = False 21 | 22 | 23 | relative_time = (date - first_timestamp).total_seconds() 24 | 25 | f2.write(','.join(["{:.6f}".format(relative_time), line[1]])) -------------------------------------------------------------------------------- /Lecture 5/Homework 2/star_data_documentation.txt: -------------------------------------------------------------------------------- 1 | Columns: 2 | - Name - Name of the star 3 | - RA - Right Ascension in degrees, ICRS coord. (J2000) 4 | - Dec - Declination in degrees, ICRS coord. (J2000) 5 | - pa_RA - Proper motion in right ascension, in miliarcseconds per year 6 | - pa_dec - Proper motion in declination, in miliarcseconds per year 7 | - Vr - radial velocity in kilometers per second, a positive value means that the star is moving away from us 8 | - parallax - parallax of the star in miliarcseconds, HINT: to convert the parallax to the distance in parsecs, use: d = 1/parallax 9 | 10 | Other values: 11 | 1 parsec = 3.0857e13 km 12 | 1 parsec = 2.0626481e5 AU 13 | 1 parsec = 3.26156 ly 14 | 1 AU = 1.495978707e8 km 15 | 1 ly = 9.4607304725808e12 km 16 | -------------------------------------------------------------------------------- /Lecture 5/Homework 2/star_data.txt: -------------------------------------------------------------------------------- 1 | Name,RA(deg),Dec(deg),pm_RA(mas/yr),pm_Dec(mas/yr),Vr(km/s),parallax(mas) 2 | Wolf 359,164.120271,+07.014658,-3842.0,-2725.0,19.321,418.3 3 | Proxima Centauri,217.42895219,-62.67948975,-3775.75,765.54,-22.40,768.13 4 | Alpha Centauri,219.900850,-60.835619,-3608,686,-22.3,742 5 | Barnard's star,269.45207511,+04.69339088,-798.58,10328.12,-110.51,548.31 6 | Gliese 445,176.92240640,+78.69116300,743.61,481.40,-111.65,186.86 7 | Luhman 16A,150.8218675,-53.319405556,-2754.77,358.72,23.1,500.51 8 | Sirius,101.28715533,-16.71611586,-546.01,-1223.07,-5.50,379.21 9 | Lalande 21185,165.83414166,+35.96988004,-580.27,-4765.85,-84.69,392.64 10 | Ross 248,355.479122,+44.177994,115.10,-1592.77,-77.715,316.7 11 | Gliese 65,24.756054,-17.950569,3321,562,29,373.70 -------------------------------------------------------------------------------- /Lecture 3/Homework/L3_homework_generate_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import random 4 | 5 | 6 | data_dir = 'data' 7 | 8 | prefix = 'Cluster' 9 | data_files = ['original', 'modeled', 'fitted', 'final'] 10 | 11 | 12 | # Remove old data dir if it exists 13 | if os.path.isdir(data_dir): 14 | shutil.rmtree(data_dir) 15 | 16 | # Make a new data dir 17 | os.mkdir(data_dir) 18 | 19 | for i in range(50): 20 | 21 | for file_type in data_files: 22 | 23 | start = random.randint(0, 359) 24 | end = random.randint(start+1, 360) 25 | 26 | file_name = "_".join(map(str, [prefix, i+1, 'sol', start, end, file_type])) + '.txt' 27 | 28 | with open(os.path.join(data_dir, file_name), 'w') as f: 29 | for j in range(5): 30 | f.write(str(random.gauss(i, j))+'\n') 31 | 32 | -------------------------------------------------------------------------------- /Lecture 6/Homework 1/conv.py: -------------------------------------------------------------------------------- 1 | file_name = 'data.txt' 2 | 3 | 4 | with open(file_name) as f: 5 | 6 | with open('galaxies.csv', 'w') as f2: 7 | 8 | first_line = True 9 | 10 | for line in f: 11 | 12 | if first_line: 13 | f2.write(line) 14 | first_line = False 15 | continue 16 | 17 | name, dist, vr = line.split(',') 18 | 19 | if vr.replace('\n', '') == 'NA': 20 | continue 21 | 22 | dist = 10**(1+float(dist)/5)/1e6 23 | 24 | ### Take galaxies which are in the linear zone 25 | 26 | # Skip all galaxies which are further away than 400 MPc 27 | if dist > 400: 28 | continue 29 | 30 | # Skip all galaxies which are further away than 400 MPc 31 | if dist < 20: 32 | continue 33 | ### 34 | 35 | # Skip velocities larger than 40k km/s 36 | if int(vr.replace('\n', '')) > 40000: 37 | continue 38 | 39 | f2.write(','.join([name, str(dist), vr])) 40 | 41 | -------------------------------------------------------------------------------- /Lecture 2/Task 1/L2_T1_text.txt: -------------------------------------------------------------------------------- 1 | Titius-Bode law 2 | --------------- 3 | 4 | Make a list of planet distances from the Sun (semimajor axis) using the Bode’s law and calculate the difference in % from the real planet’s distance. 5 | 6 | Read more about Bode’s law to understand how to approach the problem: https://en.wikipedia.org/wiki/Titius%E2%80%93Bode_law 7 | 8 | Here is how you can calculate the deviation percentage: 9 | 10 | Deviation percentage = (observed - expected)/expected * 100 11 | 12 | Steps for solving the problem: 13 | 14 | - Find the formula for calculating planet distances using Bode’s law: a = 0.4 + 0.3*2^m 15 | 16 | - Make a list of “m’s”: -inf, 0, 1, 2… 7 (question: how can we roughly approximate minus infinity?) 17 | 18 | - Make a list of known planet distances 19 | 20 | - Go through each “m” for each planet and calculate its predicted distance and the difference in % from the known distance: diff = (known_distance - predicted)/predicted*100% -------------------------------------------------------------------------------- /Lecture 7/TextFileParser.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | def parseTextFile(file_name, delimiter=",", header=0): 4 | """ Parse a text file to a list. The file contents are delimited and have a header. """ 5 | 6 | with open(file_name) as f: 7 | 8 | # Skip the header 9 | for i in range(header): 10 | next(f) 11 | 12 | 13 | data = [] 14 | 15 | # Parse file contents 16 | for line in f: 17 | 18 | # Remove the newline char 19 | line = line.replace('\n', '').replace('\r', '') 20 | 21 | # Split the line by the delimiter 22 | line = line.split(delimiter) 23 | 24 | # Strip whitespaces from individual entries in the line 25 | for i, entry in enumerate(line): 26 | line[i] = entry.strip() 27 | 28 | # Add the contents of the line to the data list 29 | data.append(line) 30 | 31 | return data 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | 37 | file_name = 'data.txt' 38 | 39 | print(parseTextFile(file_name, header=1)) -------------------------------------------------------------------------------- /Lecture 4/Homework 1/L4_homework1_generate_data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import matplotlib.pyplot as plt 4 | 5 | # Generate the data 6 | x = np.linspace(np.random.randint(0, 10), np.random.randint(195, 210), 10000) 7 | 8 | # Noise 9 | y = (0.5*np.sin(2*x) + 5*np.random.random(x.size)) + np.cos(0.5*x + 0.1) + 0.3*np.cos(x + 0.3) 10 | 11 | # Add a few signals 12 | start = 1000 13 | stop = 1310 14 | y[start:stop] += 7*np.sin(0.01*(np.arange(start, stop)-start)) 15 | 16 | start = 2000 17 | stop = 2310 18 | y[start:stop] += 10*np.sin(0.01*(np.arange(start, stop)-start)) 19 | 20 | start = 5000 21 | stop = 5310 22 | y[start:stop] += 20*np.sin(0.01*(np.arange(start, stop)-start)) 23 | 24 | start = 8300 25 | stop = 8610 26 | y[start:stop] += 5*np.sin(0.01*(np.arange(start, stop)-start)) 27 | 28 | plt.plot(x, y) 29 | plt.show() 30 | 31 | # Save the data to a file 32 | data = np.column_stack((x, y)) 33 | 34 | np.savetxt('data.txt', data, fmt='%.4f', delimiter=',', header='Time,Signal') -------------------------------------------------------------------------------- /Lecture 4/Task 1/TextFileParser.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | def parseTextFile(file_name, delimiter=",", header=0): 4 | """ Parse a text file to a list. The file contents are delimited and have a header. """ 5 | 6 | with open(file_name) as f: 7 | 8 | # Skip the header 9 | for i in range(header): 10 | next(f) 11 | 12 | 13 | data = [] 14 | 15 | # Parse file contents 16 | for line in f: 17 | 18 | # Remove the newline char 19 | line = line.replace('\n', '').replace('\r', '') 20 | 21 | # Split the line by the delimiter 22 | line = line.split(delimiter) 23 | 24 | # Strip whitespaces from individual entries in the line 25 | for i, entry in enumerate(line): 26 | line[i] = entry.strip() 27 | 28 | # Add the contents of the line to the data list 29 | data.append(line) 30 | 31 | return data 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | 37 | file_name = 'data.txt' 38 | 39 | print(parseTextFile(file_name, header=1)) -------------------------------------------------------------------------------- /Lecture 6/Task 1/TextFileParser.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | def parseTextFile(file_name, delimiter=",", header=0): 4 | """ Parse a text file to a list. The file contents are delimited and have a header. """ 5 | 6 | with open(file_name) as f: 7 | 8 | # Skip the header 9 | for i in range(header): 10 | next(f) 11 | 12 | 13 | data = [] 14 | 15 | # Parse file contents 16 | for line in f: 17 | 18 | # Remove the newline char 19 | line = line.replace('\n', '').replace('\r', '') 20 | 21 | # Split the line by the delimiter 22 | line = line.split(delimiter) 23 | 24 | # Strip whitespaces from individual entries in the line 25 | for i, entry in enumerate(line): 26 | line[i] = entry.strip() 27 | 28 | # Add the contents of the line to the data list 29 | data.append(line) 30 | 31 | return data 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | 37 | file_name = 'data.txt' 38 | 39 | print(parseTextFile(file_name, header=1)) -------------------------------------------------------------------------------- /Lecture 5/Homework 2/TextFileParser.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | def parseTextFile(file_name, delimiter=",", header=0): 4 | """ Parse a text file to a list. The file contents are delimited and have a header. """ 5 | 6 | with open(file_name) as f: 7 | 8 | # Skip the header 9 | for i in range(header): 10 | next(f) 11 | 12 | 13 | data = [] 14 | 15 | # Parse file contents 16 | for line in f: 17 | 18 | # Remove the newline char 19 | line = line.replace('\n', '').replace('\r', '') 20 | 21 | # Split the line by the delimiter 22 | line = line.split(delimiter) 23 | 24 | # Strip whitespaces from individual entries in the line 25 | for i, entry in enumerate(line): 26 | line[i] = entry.strip() 27 | 28 | # Add the contents of the line to the data list 29 | data.append(line) 30 | 31 | return data 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | 37 | file_name = 'data.txt' 38 | 39 | print(parseTextFile(file_name, header=1)) -------------------------------------------------------------------------------- /Lecture 6/Homework 1/TextFileParser.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | def parseTextFile(file_name, delimiter=",", header=0): 4 | """ Parse a text file to a list. The file contents are delimited and have a header. """ 5 | 6 | with open(file_name) as f: 7 | 8 | # Skip the header 9 | for i in range(header): 10 | next(f) 11 | 12 | 13 | data = [] 14 | 15 | # Parse file contents 16 | for line in f: 17 | 18 | # Remove the newline char 19 | line = line.replace('\n', '').replace('\r', '') 20 | 21 | # Split the line by the delimiter 22 | line = line.split(delimiter) 23 | 24 | # Strip whitespaces from individual entries in the line 25 | for i, entry in enumerate(line): 26 | line[i] = entry.strip() 27 | 28 | # Add the contents of the line to the data list 29 | data.append(line) 30 | 31 | return data 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | 37 | file_name = 'data.txt' 38 | 39 | print(parseTextFile(file_name, header=1)) -------------------------------------------------------------------------------- /Lecture 8/L8_example_subprocess.py: -------------------------------------------------------------------------------- 1 | """ In this script, an example how to call external scripts and commands is given. """ 2 | 3 | import subprocess 4 | 5 | 6 | # Full path to script you would like to call 7 | script_path = './test.sh' 8 | 9 | 10 | # If you just want to call an external script or command, you can do this: 11 | subprocess.call(script_path, shell=True) 12 | 13 | 14 | # And that's it! 15 | 16 | ############### 17 | 18 | # Calling a script and getting the result requires a bit more commands: 19 | 20 | 21 | # Let us define a command we would like to call, with all the extra options 22 | command = 'uname -a' 23 | 24 | # Call the script 25 | p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 26 | 27 | # Talk with date command i.e. read data from stdout and stderr. Store this info in tuple. 28 | (output, err) = p.communicate() 29 | 30 | # Wait for date to terminate. Get return returncode # 31 | p_status = p.wait() 32 | 33 | # Print script output 34 | print "Command output : ", output -------------------------------------------------------------------------------- /Lecture 7/misc/make_radiometer_data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import scipy.signal 4 | 5 | 6 | 7 | def func(x): 8 | 9 | signal = np.zeros_like(x) 10 | 11 | base_freq = 50 12 | amps = [2800, 2132, 867, 195] 13 | 14 | for i, amp in enumerate(amps): 15 | signal += amp*np.sin((i+1)*(base_freq*2*np.pi)*x) 16 | 17 | 18 | signal += 31*np.sin(192*2*np.pi*x) 19 | signal += 101*np.sin(242*2*np.pi*x) 20 | 21 | st = 2515 22 | en = 2670 23 | signal[st:en] += 200*np.sin(8*x[st:en] + np.pi + 0.7) 24 | 25 | 26 | # Add noise 27 | signal += 20*np.random.random(size=len(x)) 28 | 29 | 30 | 31 | 32 | return signal 33 | 34 | 35 | file_name = 'radiometer_data.csv' 36 | 37 | x_data = np.arange(0, 10.24, 1.0/500) 38 | 39 | y_data = func(x_data) 40 | 41 | with open(file_name, 'w') as f: 42 | 43 | for x, y in zip(x_data, y_data): 44 | 45 | f.write("{:.6f},{:.6f}".format(x, y)+'\n') 46 | 47 | plt.plot(x_data, y_data) 48 | plt.show() 49 | 50 | -------------------------------------------------------------------------------- /Lecture 2/data.txt: -------------------------------------------------------------------------------- 1 | Num,Name,Epoch,q,e,i,w,Node,Tp,Ref 2 | 1P,Halley ,49400,0.58597811,0.96714291,62.26269,111.33249, 58.42008,19860205.89532,JPL J863/77 3 | 2P,Encke ,57720,0.33590577,0.84833349,11.77837,186.56104,334.56111,20170310.09194,JPL K173/2 4 | 3D,Biela ,-9480,0.87907300,0.75129900,13.21640,221.65880,250.66900,18321126.61520,IAUCAT03 5 | 4P,Faye ,57262,1.65187634,0.56961755, 9.07029,205.12582,199.14398,20140529.86872,JPL K144/7 6 | 5D,Brorsen , 7440,0.58984700,0.80979600,29.38210, 14.94680,102.96760,18790331.03410,IAUCAT03 7 | 6P,d'Arrest ,57200,1.36145455,0.61142192,19.48168,178.11523,138.93371,20150302.41870,JPL K155/3 8 | 7P,Pons-Winnecke,57380,1.23896144,0.63761701,22.33509,172.50006, 93.40960,20150130.53022,JPL K088/17 9 | 8P,Tuttle ,54374,1.02711659,0.81979975,54.98318,207.50925,270.34165,20080127.02555,JPL K074/27 10 | 9P,Tempel 1 ,57375,1.54236947,0.50981920,10.47332,179.18727, 68.76828,20160802.55955,JPL 160 11 | 10P,Tempel 2 ,57262,1.41764523,0.53735240,12.02891,195.54767,117.80539,20151114.25866,JPL K1013/12 -------------------------------------------------------------------------------- /Lecture 3/data.txt: -------------------------------------------------------------------------------- 1 | Num,Name,Epoch,q,e,i,w,Node,Tp,Ref 2 | 1P,Halley ,49400,0.58597811,0.96714291,62.26269,111.33249, 58.42008,19860205.89532,JPL J863/77 3 | 2P,Encke ,57720,0.33590577,0.84833349,11.77837,186.56104,334.56111,20170310.09194,JPL K173/2 4 | 3D,Biela ,-9480,0.87907300,0.75129900,13.21640,221.65880,250.66900,18321126.61520,IAUCAT03 5 | 4P,Faye ,57262,1.65187634,0.56961755, 9.07029,205.12582,199.14398,20140529.86872,JPL K144/7 6 | 5D,Brorsen , 7440,0.58984700,0.80979600,29.38210, 14.94680,102.96760,18790331.03410,IAUCAT03 7 | 6P,d'Arrest ,57200,1.36145455,0.61142192,19.48168,178.11523,138.93371,20150302.41870,JPL K155/3 8 | 7P,Pons-Winnecke,57380,1.23896144,0.63761701,22.33509,172.50006, 93.40960,20150130.53022,JPL K088/17 9 | 8P,Tuttle ,54374,1.02711659,0.81979975,54.98318,207.50925,270.34165,20080127.02555,JPL K074/27 10 | 9P,Tempel 1 ,57375,1.54236947,0.50981920,10.47332,179.18727, 68.76828,20160802.55955,JPL 160 11 | 10P,Tempel 2 ,57262,1.41764523,0.53735240,12.02891,195.54767,117.80539,20151114.25866,JPL K1013/12 -------------------------------------------------------------------------------- /Lecture 4/Task 1/data.txt: -------------------------------------------------------------------------------- 1 | Num,Name,Epoch,q,e,i,w,Node,Tp,Ref 2 | 1P,Halley ,49400,0.58597811,0.96714291,62.26269,111.33249, 58.42008,19860205.89532,JPL J863/77 3 | 2P,Encke ,57720,0.33590577,0.84833349,11.77837,186.56104,334.56111,20170310.09194,JPL K173/2 4 | 3D,Biela ,-9480,0.87907300,0.75129900,13.21640,221.65880,250.66900,18321126.61520,IAUCAT03 5 | 4P,Faye ,57262,1.65187634,0.56961755, 9.07029,205.12582,199.14398,20140529.86872,JPL K144/7 6 | 5D,Brorsen , 7440,0.58984700,0.80979600,29.38210, 14.94680,102.96760,18790331.03410,IAUCAT03 7 | 6P,d'Arrest ,57200,1.36145455,0.61142192,19.48168,178.11523,138.93371,20150302.41870,JPL K155/3 8 | 7P,Pons-Winnecke,57380,1.23896144,0.63761701,22.33509,172.50006, 93.40960,20150130.53022,JPL K088/17 9 | 8P,Tuttle ,54374,1.02711659,0.81979975,54.98318,207.50925,270.34165,20080127.02555,JPL K074/27 10 | 9P,Tempel 1 ,57375,1.54236947,0.50981920,10.47332,179.18727, 68.76828,20160802.55955,JPL 160 11 | 10P,Tempel 2 ,57262,1.41764523,0.53735240,12.02891,195.54767,117.80539,20151114.25866,JPL K1013/12 -------------------------------------------------------------------------------- /Lecture 6/Task 1/meteor_data.txt: -------------------------------------------------------------------------------- 1 | Time, Lag 2 | 1.5415883e-01,2.0463764e+01 3 | 1.5508986e-01,1.3734804e+00 4 | 1.6328073e-01,2.0903137e+01 5 | 1.6420579e-01,7.6021637e+00 6 | 1.7240071e-01,2.0275089e+01 7 | 1.7333078e-01,1.6151095e+01 8 | 1.8154287e-01,2.5430807e+01 9 | 1.9065475e-01,4.1516221e+01 10 | 1.9977283e-01,4.1820752e+01 11 | 2.0889187e-01,5.9158994e+01 12 | 2.1801376e-01,5.8598367e+01 13 | 2.2714090e-01,6.8223147e+01 14 | 2.3626375e-01,7.8696230e+01 15 | 2.4539471e-01,7.9455851e+01 16 | 2.5450778e-01,8.7583403e+01 17 | 2.6363277e-01,9.7132335e+01 18 | 2.7275085e-01,1.1043687e+02 19 | 2.8187871e-01,1.1308693e+02 20 | 2.9099274e-01,1.4824819e+02 21 | 3.0011892e-01,1.5483926e+02 22 | 3.0924392e-01,1.7338819e+02 23 | 3.1836486e-01,1.6979385e+02 24 | 3.2748771e-01,1.8426694e+02 25 | 3.3661175e-01,2.1778216e+02 26 | 3.4573388e-01,2.2822996e+02 27 | 3.5485673e-01,2.4570304e+02 28 | 3.6397791e-01,2.7011713e+02 29 | 3.7310171e-01,2.8962393e+02 30 | 3.8223171e-01,3.1434984e+02 31 | 3.9134479e-01,3.3447739e+02 32 | 4.0046692e-01,3.6392519e+02 33 | 4.0960574e-01,3.9696292e+02 34 | 4.1871977e-01,4.1912418e+02 35 | 4.2783880e-01,4.5346243e+02 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Denis Vida 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lecture 5/Homework 2/L5_homework2_text.txt: -------------------------------------------------------------------------------- 1 | Background story: I have recently read that one of the stars covered by the Kepler K3 mission is Wolf 359, one of the nearest stars to us. I have found that name somehow familiar, Wolf 359, hmmm…. Ah yes, that is the name of the famous Star Trek battle, the first major engagement with the Borg! I proceeded to google ‘Wolf 359’ and read the Wiki article about it. It turns out it is a very interesting star, but this graph caught my attention: https://en.wikipedia.org/wiki/Wolf_359#/media/File:Near-stars-past-future-en.svg 2 | 3 | At that moment I realized that making this graph would be an awesome homework assignment! 4 | 5 | You are provided with a star catalog in a text format, which was pulled from SIMBAD. Use the TextFileParser module you wrote for Lecture 2 Task 3. In the data file, you are given the right ascension and declination of the star, its radial velocity, proper motion and parallax. This information should be enough for you to calculate its position and velocity vector relative to us. 6 | 7 | Make a plot of distances to the nearest stars from the 60,000 years in the past to 100,000 years in the future. 8 | 9 | On this link, you will find everything you need about calculating the position of the stars and their velocities: http://www.astronexus.com/a-a/motions-long-term -------------------------------------------------------------------------------- /Lecture 6/Task 1/L6_T1_solution.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | import scipy.optimize 5 | import matplotlib.pyplot as plt 6 | 7 | from TextFileParser import parseTextFile 8 | 9 | 10 | def meteor_model(x, a, b, c, d): 11 | """ Time vs. meteor lag. """ 12 | 13 | return a*np.exp(b*x) + c*x + d 14 | 15 | 16 | 17 | if __name__ == "__main__": 18 | 19 | # File name of the data file 20 | file_name = 'meteor_data.txt' 21 | 22 | # Load the data 23 | star_data = parseTextFile(file_name, header=1) 24 | 25 | # Convert to float numpy array 26 | star_data = np.array(star_data, dtype=np.float64) 27 | 28 | # Extract x and y data 29 | x = star_data[:,0] 30 | y = star_data[:,1] 31 | 32 | # Fit the model 33 | popt, pconv = scipy.optimize.curve_fit(meteor_model, x, y) 34 | 35 | print(popt) 36 | 37 | # Plot original data 38 | plt.scatter(x, y) 39 | 40 | # Generate new X data (original data is not sorted and not well distributed) 41 | x_plot = np.linspace(x.min(), x.max(), 100) 42 | 43 | # Plot fitted model 44 | plt.plot(x_plot, meteor_model(x_plot, *popt)) 45 | 46 | plt.show() 47 | 48 | 49 | # Calculate residuals 50 | res = y - meteor_model(x, *popt) 51 | 52 | plt.scatter(x, res) 53 | 54 | plt.grid() 55 | plt.show() -------------------------------------------------------------------------------- /Lecture 4/Homework 1/L4_homework1_text.txt: -------------------------------------------------------------------------------- 1 | In this homework, you will learn how to take data from a text file and plot it on a graph. Use the L4_homework1_generate_data.py file to generate the data.txt file. If you open the file, you will see that if contains time series data (time vs. signal), and that there is a one line header. Use the function from the TextFileParser module to load the data into Python, then convert the data into a numpy array (float64). You will also have to separate two columns of the numpy array into two arrays. Here are the first three lines of the solution, which will help you get started: 2 | 3 | # Load the data 4 | 5 | data = parseTextFile('data.txt', header=1) 6 | 7 | # Convert the data to a numpy array, as floats 8 | 9 | data = np.array(data).astype(np.float64) 10 | 11 | # Split the data into time and signal 12 | 13 | time, signal = np.hsplit(data, 2) 14 | 15 | The array ‘time’ will hold the timing information (that is our X axis of the plot), and ‘signal’ is our Y axis data. Plot time vs. signal. 16 | 17 | Next, plot a solid red horizontal line which will show the mean value of the data. Then plot a red dashed horizontal line which will show the level of 2 sigma (standard deviations) above the mean value. Turn on the grid, and label axes, and make a title for the plot. I have attached the resulting plot, so you should aim to reproduce it. -------------------------------------------------------------------------------- /Lecture 3/Homework/L3_homework_text.txt: -------------------------------------------------------------------------------- 1 | You were running your code on a supercomputer for 2 months, and it is finally done, the data is in! But alas, your code has put all data in the same folder, but you need it organized by individual directories. Instead of spending hours making new directories and sorting the data, you feel confident enough to write a Python script which will do the sorting for you! 2 | 3 | You were running some cutting edge clustering algorithm on the supercomputer, and there are 4 files for each cluster: *_modeled.txt, *_original.txt, *_fitted.txt and *_final.txt file. Different clusters are numerated in the following way: Cluster_1*, Cluster_2*, Cluster_3*, etc. 4 | 5 | Thus, make a new directory for each cluster (name it Cluster_n, where n is the number of the cluster), and sort all files belonging to that cluster into each directory. 6 | 7 | The resulting directory structure should look like this: 8 | 9 | data/ 10 | Cluster_1/ 11 | Cluster_1_sol_165_216_modeled.txt 12 | Cluster_1_sol_185_272_original.txt 13 | 14 | Cluster_1_sol_212_278_fitted.txt 15 | 16 | Cluster_1_sol_289_341_final.txt 17 | 18 | Cluster_2/ 19 | 20 | Cluster_2_sol_16_323_final.txt 21 | 22 | Cluster_2_sol_17_125_original.txt 23 | 24 | Cluster_2_sol_120_252_fitted.txt 25 | 26 | Cluster_2_sol_136_216_modeled.txt 27 | 28 | Cluster_3/ 29 | 30 | ... 31 | 32 | Hint: You will first have to find the names of clusters present in the data, you can do that by going through each file name and adding to it only those numbers which you didn’t encounter before. -------------------------------------------------------------------------------- /Lecture 5/Homework 1/L5_homework1_text.txt: -------------------------------------------------------------------------------- 1 | Write a script which will generate a 150x150 Ulam spiral as an image: https://en.wikipedia.org/wiki/Ulam_spiral 2 | 3 | More on Ulam spirals: https://www.youtube.com/watch?v=iFuR97YcSLM&feature=youtu.be 4 | 5 | You will need to make functions for: 6 | ------------------------------------ 7 | 8 | - Making the next step in the spiral (X, Y position, direction, number of steps) - the easiest approach is to assume that every (x, Y) pixel on the image represents a certain state of the spiral, and that you can apply to that state a certain function which will always give you the next step in the spiral. Let’s say we are generating a 100x100 spiral. This would be the start the the spiral (numbers at the left and bottom represent the Y and X coordinates of the position of the number on the image): 9 | 10 | # Y 11 | 12 | # 52 --13 13 | 14 | # | 15 | 16 | # 51 5--4--3 12 17 | 18 | # | | | 19 | 20 | # 50 6 1--2 11 21 | 22 | # | | 23 | 24 | # 49 7--8--9--10 25 | 26 | # X 49 50 51 52 27 | 28 | So when we are at (50, 50), the current number is 1, the next direction in right. After we take one step, the current number is 2, we are at (51, 50) and the next direction is up, etc. After that, the number is 3, position is (51, 51) and the next direction is left... 29 | 30 | Try to find a rule which you can use to calculate when the change in direction happens, after that, it is all very simple. 31 | 32 | 33 | - Checking if the given number is a prime (can even numbers be prime? do you really need to try to divide by all numbers up to the given number to check that it is prime?) 34 | 35 | - Generating an image -------------------------------------------------------------------------------- /Lecture 3/Task 1/L3_T1_text.txt: -------------------------------------------------------------------------------- 1 | Use the L3_T1_generate_data.py script to generate the 'data' folder (be sure to put it into a separate folder, e.g. in "Lecture 3/Task 1" folder, so it does not mess up your other files in the Lecture 3 folder. 2 | 3 | In the folder you have files named "HL_fri_nov_25_00-04-15_2016_033253.eps.png.txt”, "HL_sat_nov_26_06-37-08_2016_089369.eps.png.txt”, "HL_thu_nov_24_18-30-04_2016_566493.eps.png.txt”, ... 4 | 5 | When you sort the files in the directory, they are not sorted by their time (Monday, Tuesday, Wednesday, etc.) but alphabetically, with puts Friday in front of Monday! Make a script which will rename the files in a way so the days are replaced with their respective ordinal numbers, 1 for Monday, 2 for Tuesday, etc. 6 | 7 | Original name | Fixed name 8 | --------------------------------------------------------------------------------------------- 9 | HL_fri_nov_25_00-04-15_2016_033253.eps.png.txt | HL_5_nov_25_00-04-15_2016_033253.eps.png.txt 10 | 11 | HL_sat_nov_26_06-37-08_2016_089369.eps.png.txt | HL_6_nov_26_06-37-08_2016_089369.eps.png.txt 12 | 13 | HL_thu_nov_24_18-30-04_2016_566493.eps.png.txt | HL_4_nov_24_18-30-04_2016_566493.eps.png.txt 14 | 15 | --------------------------------------------------------------------------------------------- 16 | 17 | 18 | Steps for solving the problem: 19 | 20 | - Get a list of all files in the directory 21 | 22 | - Break up the name of individual pieces 23 | 24 | - Extract the day name 25 | 26 | - Replace the day name with its ordinal number 27 | 28 | - Rename the file 29 | 30 | 31 | If you mess up the original data, feel free to delete the data folder, and run the L3_T1_generate_data.py script, which will generate the data from scratch! -------------------------------------------------------------------------------- /Lecture 7/spline_data.csv: -------------------------------------------------------------------------------- 1 | 11.875000,-10.953469 2 | 11.934149,-8.117969 3 | 11.993299,-6.596622 4 | 12.052448,-3.354321 5 | 12.111597,-0.958482 6 | 12.170747,-2.324702 7 | 12.229896,-0.660886 8 | 12.289046,-1.454667 9 | 12.348195,-1.958678 10 | 12.407344,-4.446010 11 | 12.466494,-3.996702 12 | 12.525643,-5.877253 13 | 12.584792,-6.421844 14 | 12.643942,-4.886846 15 | 12.703091,-6.068329 16 | 12.762241,-3.952618 17 | 12.821390,-3.466685 18 | 12.880539,-3.803763 19 | 12.939689,-3.054874 20 | 12.998838,-2.772091 21 | 13.057987,-3.121008 22 | 13.117137,-3.495554 23 | 13.176286,-4.678486 24 | 13.235435,-3.433297 25 | 13.294585,-0.067265 26 | 13.353734,1.942072 27 | 13.412884,3.628009 28 | 13.472033,8.257841 29 | 13.531182,10.842555 30 | 13.590332,14.337392 31 | 13.649481,14.056718 32 | 13.708630,14.671432 33 | 13.767780,14.717292 34 | 13.826929,15.135277 35 | 13.886078,12.265920 36 | 13.945228,12.948697 37 | 14.004377,11.063161 38 | 14.063527,9.866508 39 | 14.122676,9.421426 40 | 14.181825,9.612442 41 | 14.240975,9.813783 42 | 14.300124,10.534346 43 | 14.359273,11.059253 44 | 14.418423,10.180964 45 | 14.477572,6.263420 46 | 14.536722,5.495562 47 | 14.595871,5.000401 48 | 14.655020,4.055649 49 | 14.714170,5.041868 50 | 14.773319,3.555898 51 | 14.832468,5.685627 52 | 14.891618,5.604242 53 | 14.950767,8.781500 54 | 15.009916,11.027518 55 | 15.069066,10.969036 56 | 15.128215,10.883791 57 | 15.187365,10.882441 58 | 15.246514,8.050942 59 | 15.305663,7.017310 60 | 15.364813,6.405354 61 | 15.423962,3.363961 62 | 15.483111,1.625242 63 | 15.542261,-1.699632 64 | 15.601410,-2.151555 65 | 15.660559,-4.395696 66 | 15.719709,-2.321577 67 | 15.778858,-5.893422 68 | 15.838008,-7.053614 69 | 15.897157,-6.483041 70 | 15.956306,-8.792884 71 | 16.015456,-11.059966 72 | 16.074605,-12.857222 73 | 16.133754,-13.533929 74 | 16.192904,-13.394411 75 | 16.252053,-14.006074 76 | 16.311203,-10.949304 77 | 16.370352,-11.509029 78 | 16.429501,-6.783871 79 | 16.488651,-5.234504 80 | 16.547800,-2.014852 81 | -------------------------------------------------------------------------------- /ParseTextFile.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | 4 | 5 | def parseData(file_name, delimiter=None, header_size=0, col_types=None, ret_array=False): 6 | """ Parse data form a text file 7 | 8 | Arguments: 9 | file_name: [str] Name of the input file. 10 | 11 | Keyword arguments: 12 | delimiter: [str] Data delimiter (often a comma of a semicolon). None by default, i.e. space/tab 13 | delimited data 14 | header_size: [int] Number of lines in the header of the file. 0 by defualt. 15 | col_types: [type, or list of types] Define which columns are of which type. E.g. if all colums contain 16 | floating point data, then you can specify: 17 | col_types=float. 18 | On the other hand, if the first colum 19 | contains integer values, and second column contains floating point values, you can specify: 20 | col_types=[int, float] 21 | This argument is None by default, meaning that values will be left as strings. 22 | ret_array: [bool] If True, the function returns a numpy array. If False, it returns a Pyhon list. 23 | Be aware that if col_types are specified, and one of the types is float, the whole array will be 24 | a float array. Furthermore, if some values in the read data are strings, the all values in the 25 | numpy array will be strings are well. 26 | 27 | Returns: 28 | data_list: Python list if ret_array is False, numpy array if ret_array is True 29 | 30 | """ 31 | 32 | with open(file_name) as f: 33 | 34 | # Skip header 35 | for i in range(header_size): 36 | next(f) 37 | 38 | data_list = [] 39 | 40 | # Go through every line of the file 41 | for line in f: 42 | 43 | line = line.replace('\n', '').replace('\r', '') 44 | 45 | # Split the line by the given delimiter 46 | if delimiter is None: 47 | line = line.split() 48 | 49 | else: 50 | line = line.split(delimiter) 51 | 52 | 53 | # Convert the columns to given types 54 | if col_types is not None: 55 | 56 | if not isinstance(col_types, list): 57 | col_types = [col_types]*len(line) 58 | 59 | 60 | if len(line) == len(col_types): 61 | 62 | for i, (tp, entry) in enumerate(zip(col_types, line)): 63 | line[i] = tp(entry) 64 | 65 | 66 | data_list.append(line) 67 | 68 | # Convert the data to a numpy array 69 | if ret_array: 70 | data_list = np.array(data_list) 71 | 72 | 73 | return data_list 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Lecture 10/AverageImage.pyx: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Import cython libraries 4 | cimport cython 5 | import numpy as np 6 | cimport numpy as np 7 | 8 | 9 | # Define cython types for numpy arrays 10 | FLOAT_TYPE = np.float64 11 | ctypedef np.float64_t FLOAT_TYPE_t 12 | 13 | 14 | #@cython.boundscheck(False) # This disables checking that the indices of an array are valid 15 | #@cython.wraparound(False) # This disables negative indexing, e.g. array[-1] 16 | #@cython.cdivision(True) # This disables checks for divisions by zero 17 | def cyAverageImg(np.ndarray[FLOAT_TYPE_t, ndim=2] img, int region): 18 | """ Averages image pixels in (region)x(region) neighbourhood. 19 | 20 | Arguments: 21 | img: [2D ndarray] image as numpy array 22 | region: [int] averaging neighbourhood, should be an odd number (3, 5, 7, 9, etc.) 23 | 24 | Return: 25 | img_avg: [2D ndarray] averaged image 26 | """ 27 | 28 | cdef int reg_r = region//2 29 | 30 | # Output image 31 | cdef np.ndarray[FLOAT_TYPE_t, ndim=2] img_avg = np.zeros_like(img) 32 | 33 | cdef int x_size = img.shape[0] 34 | cdef int y_size = img.shape[1] 35 | 36 | cdef double s = 0 37 | cdef int i, j, m, n, x, y 38 | 39 | # Average pixels in the 3x3 region 40 | for i in range(x_size): 41 | for j in range(y_size): 42 | 43 | s = 0 44 | for x in range(-reg_r, reg_r + 1): 45 | for y in range(-reg_r, reg_r + 1): 46 | 47 | m = i + x 48 | n = j + y 49 | 50 | # Wrap the borders 51 | if m >= x_size: 52 | m = m%x_size 53 | 54 | if n >= y_size: 55 | n = n%y_size 56 | 57 | 58 | s += img[m, n] 59 | 60 | 61 | img_avg[i, j] = s/(region**2) 62 | 63 | 64 | return img_avg 65 | 66 | 67 | 68 | # Import the square root function from a C library 69 | from libc.math cimport sqrt 70 | 71 | # cpdef means that the function will be accessible both inside this module and to external Python modules. 72 | # If we were to write "cdef", wihtout the "p", then the function would be only available inside this module. 73 | cpdef double cyEuclidDist(double x1, double y1, double x2, double y2): 74 | """ Calculate the Euclidian distance of two points in 2D. """ 75 | 76 | return sqrt((x1 - x2)**2 + (y1 - y2)**2) -------------------------------------------------------------------------------- /Lecture 9/PyDomainParallelizer.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import multiprocessing 4 | 5 | def DomainParallelizer(domain, function, cores, kwarg_dict=None): 6 | """ Runs N (cores) functions as separate processes with parameters given in the domain list. 7 | 8 | Arguments: 9 | domain: [list] a list of separate data (arguments) to feed individual function calls 10 | function: [function object] function that will be called with one entry from the domain 11 | cores: [int] number of CPU cores, or number of parallel processes to run simultaneously 12 | 13 | Keyword arguments: 14 | kwarg_dict: [dictionary] a dictionary of keyword arguments to be passed to the function, None by default 15 | 16 | Return: 17 | results: [list] a list of function results 18 | 19 | """ 20 | 21 | def _logResult(result): 22 | """ Save the result from the async multiprocessing to a results list. """ 23 | 24 | results.append(result) 25 | 26 | 27 | if kwarg_dict is None: 28 | kwarg_dict = {} 29 | 30 | 31 | results = [] 32 | 33 | 34 | # Special case when running on only one core, run without multiprocessing 35 | if cores == 1: 36 | for args in domain: 37 | results.append(function(*args, **kwarg_dict)) 38 | 39 | # Run real multiprocessing if more than one core 40 | elif cores > 1: 41 | 42 | # Generate a pool of workers 43 | pool = multiprocessing.Pool(cores) 44 | 45 | # Give workers things to do 46 | for args in domain: 47 | pool.apply_async(function, args, kwarg_dict, callback=_logResult) 48 | 49 | # Clean up 50 | pool.close() 51 | pool.join() 52 | 53 | else: 54 | print('The number of CPU cores defined is not in an expected range (1 or more.)') 55 | print('Use cpu_cores = 1 as a fallback value.') 56 | 57 | return results 58 | 59 | 60 | 61 | ############################## 62 | ## USAGE EXAMPLE 63 | 64 | 65 | import time 66 | import sys 67 | 68 | def mp_worker(name, wait_time, status_no=0): 69 | """ Example worker function. This function will print out the name of the worker and wait 'wait_time 70 | seconds. 71 | 72 | """ 73 | 74 | print("Process ", name, "Waiting", wait_time, "s, status:", status_no) 75 | 76 | time.sleep(wait_time) 77 | 78 | print("Process done:", name) 79 | 80 | # Must use if you want print to be visible on the screen! 81 | sys.stdout.flush() 82 | 83 | return int(wait_time) 84 | 85 | 86 | 87 | if __name__ == '__main__': 88 | 89 | # List of function arguments for every run 90 | data = [ 91 | ['a', 2], ['b', 4], ['c', 6], ['d', 8], 92 | ['e', 1], ['f', 3], ['g', 5], ['h', 7] 93 | ] 94 | 95 | # Get the number of cpu cores available 96 | cpu_cores = multiprocessing.cpu_count() 97 | 98 | # Run the parallelized function 99 | results = DomainParallelizer(data, mp_worker, cpu_cores, kwarg_dict={'status_no': 10}) 100 | 101 | print('Results:', results) -------------------------------------------------------------------------------- /Lecture 10/L10_lecture_cython.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import time 4 | 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | 8 | 9 | size = 256 10 | 11 | # Let's create a checkerboard image 12 | img = np.zeros((size, size)) 13 | 14 | step = 32 15 | for n in range(step): 16 | for m in range(step): 17 | img[n::2*step, m::2*step] = 1 18 | img[n + step::2*step, m + step::2*step] = 1 19 | 20 | plt.imshow(img, cmap='gray') 21 | plt.show() 22 | 23 | 24 | 25 | def averageImg(img, region): 26 | """ Averages image pixels in (region)x(region) neighbourhood. 27 | 28 | Arguments: 29 | img: [2D ndarray] image as numpy array 30 | region: [int] averaging neighbourhood, should be an odd number (3, 5, 7, 9, etc.) 31 | 32 | Return: 33 | img_avg: [2D ndarray] averaged image 34 | """ 35 | 36 | reg_r = region//2 37 | 38 | # Output image 39 | img_avg = np.zeros_like(img) 40 | 41 | x_size = img.shape[0] 42 | y_size = img.shape[1] 43 | 44 | # Average pixels in the 3x3 region 45 | for i in range(x_size): 46 | for j in range(y_size): 47 | 48 | s = 0 49 | for x in range(-reg_r, reg_r + 1): 50 | for y in range(-reg_r, reg_r + 1): 51 | 52 | m = i + x 53 | n = j + y 54 | 55 | # Wrap the borders 56 | m = m%x_size 57 | n = n%y_size 58 | 59 | s += img[m, n] 60 | 61 | 62 | img_avg[i, j] = s/(region**2) 63 | 64 | 65 | return img_avg 66 | 67 | 68 | t1 = time.clock() 69 | img_avg = averageImg(img, 11) 70 | 71 | print('Python:', time.clock() - t1) 72 | 73 | plt.imshow(img_avg, cmap='gray') 74 | plt.show() 75 | 76 | 77 | # Let's convert the averageImg function to cython code! 78 | # Convert the function in cython... 79 | 80 | 81 | # Initialize Cython-type imports are enable cython-numpy compatibility 82 | import pyximport 83 | pyximport.install(setup_args={'include_dirs':[np.get_include()]}) 84 | 85 | # Import cythonized function 86 | from AverageImage import cyAverageImg 87 | 88 | 89 | t1 = time.clock() 90 | 91 | # Run the cythonized average image function 92 | img_avg = cyAverageImg(img, 11) 93 | 94 | print('Cython:', time.clock() - t1) 95 | 96 | 97 | plt.imshow(img_avg, cmap='gray') 98 | plt.show() 99 | 100 | 101 | # To see which parts of our code are slow, we can run in terminal: 102 | 103 | # cython -a AverageImage.pyx 104 | 105 | # This will create an annotated HTML document which will color the slow lines in yellow. If you have yellow 106 | # lines which deep in some loops, you should work to simplify/optimize that code until the lines are white. 107 | # The reason you want to optimize those lines is that they are executed many times, thus they are contributing 108 | # the most to the runtime. 109 | 110 | # Lecture note: now show how to disable boundscheck, wraparound and C division 111 | 112 | 113 | 114 | # Let's write a simple mathematical function in Python 115 | def euclidDist(x1, y1, x2, y2): 116 | """ Calculate the Euclidian distance of two points in 2D. """ 117 | 118 | return np.sqrt((x1 - x2)**2 + (y1 - y2)**2) 119 | 120 | # Now, let us write a cython function which returns a single number (we can optimize those function further) 121 | 122 | from AverageImage import cyEuclidDist 123 | 124 | 125 | # Let's compare execution times between the Python and the Cython version 126 | 127 | # First the Python version 128 | t1 = time.clock() 129 | 130 | for i in range(1000): 131 | dist = euclidDist(1, 2, 3, 4) 132 | 133 | print('Python:', time.clock() - t1) 134 | 135 | # Now the Cython version 136 | t1 = time.clock() 137 | 138 | for i in range(1000): 139 | dist = cyEuclidDist(1, 2, 3, 4) 140 | 141 | print('Cython:', time.clock() - t1) -------------------------------------------------------------------------------- /Lecture 10/L10_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import matplotlib.pyplot as plt 4 | 5 | from astroquery.sdss import SDSS 6 | from astropy import units 7 | 8 | 9 | # Photoobj_fields: UGRIZ 10 | # UGRIZ: http://www1.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/community/CFHTLS-SG/docs/extra/sdsscfhtlsugriz.gif 11 | 12 | # Let's get g and r magnitudes of stars in the globular cluster M13, in the 10 arcmin radius 13 | result_table = SDSS.query_region('m13', radius=10*units.deg/60, photoobj_fields=['ra', 'dec', 'g', 'r']) 14 | 15 | # Stars in the random part of the sky, with no clusters (stars at r = 22 and g-r = 0.5 are halo stars) 16 | #result_table = SDSS.query_region('22h46m24.00s +31d42m00.0s', radius=0.5*units.deg, photoobj_fields=['ra', 'dec', 'g', 'r']) 17 | 18 | # Start in the random part of the sky (with a cluster in the halo!) - Monoceros Ring 19 | # result_table = SDSS.query_region('7h40m48.00s +32d42m00.0s', radius=0.5*units.deg, photoobj_fields=['ra', 'dec', 'g', 'r']) 20 | 21 | 22 | print(result_table) 23 | 24 | r_mags = [] 25 | gr_mags = [] 26 | 27 | for row in result_table: 28 | 29 | g = row[2] 30 | r = row[3] 31 | 32 | # Skip empty values (-9999.0) 33 | if (g < 0) or (r < 0): 34 | continue 35 | 36 | r_mags.append(r) 37 | gr_mags.append(g - r) 38 | 39 | 40 | plt.scatter(gr_mags, r_mags, s=0.05, c='black') 41 | 42 | plt.xlabel('g-r') 43 | plt.ylabel('r') 44 | 45 | plt.xlim([-1, 2.5]) 46 | plt.ylim([22, 15]) 47 | 48 | plt.show() 49 | 50 | # Blue stars are at the left, red stars are at the right. 51 | # Why? Let's say that the g magnitude of a star is 10. If the star is brighter in red, let's say r_mag = 8, 52 | # then the g-r will be 2, thus to the right of the graph. 53 | 54 | # Does this remind you of some other graph that you may have seen? Some graph that has blue start on the left 55 | # and red stars on the right? 56 | # Of course, the Hertzsprung-Russell diagram! 57 | 58 | # This is a classic HR diagram of a globular cluster, a group of star that are of the same age and distance. 59 | 60 | 61 | # Lecture note: Now show how the CMD looks like for a random part of the sky. 62 | 63 | # Lecture note: Then show how the CMD looks like for the Monoceros Ring stars. 64 | # This is now it was found that the Milky Way is in the process of merging with another galaxy. 65 | 66 | 67 | 68 | ####################### 69 | 70 | ### Dependencies ### 71 | 72 | # Dependencies are external libraries which are used by our code. 73 | # During this lecture, we were using numpy, scipy, matplotlib, scikit-learn, etc.; all Python libraries 74 | # written by other people. 75 | 76 | # OK, where's the problem here? 77 | # - Libraires are updated, they change, functions that you using the v1.0 of the library do not exist at 78 | # all in the v2.0 of the library. 79 | # - Some libraries introduce additional dependencies, and those dependences may have their own dependences, 80 | # which in turn may have their own dependences... 81 | # - Some libraries are dependant on specific versions of some other library. E.g. let's say you have a 82 | # library A which is dependant on v1.2 of B. And then you have library C which is dependant on v2.5 of 83 | # library B... 84 | # - It is possible to have circular dependencies - some libraries are dependant on one another, which cannot 85 | # be solved easily. 86 | 87 | # The whole thing is called "Dependency Hell". 88 | 89 | # Let me show you one dependency horror story: 90 | # http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/ 91 | 92 | 93 | # Takeaway: Be careful when using a lot of libraries in your code, and avoid downloading whole libraries just 94 | # for one function. 95 | 96 | # Recommendation for Python: Try to stick to libraries which are in the Anaconda package, as people make sure 97 | # they will play nice with each other. 98 | 99 | 100 | ####################### 101 | 102 | ### Cython ### 103 | 104 | # Now we will take a look how to use cython to speed up our Python code by converting it to C! 105 | 106 | -------------------------------------------------------------------------------- /Lecture 2/L2_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | # While loop 4 | 5 | # Fibonacci series 6 | a, b = 0, 1 7 | while b < 50: 8 | print(b, end=',') 9 | a, b = b, a+b 10 | print() 11 | 12 | # Many new concepts in the code above! 13 | # INDENTATION - Python's way of grouping statements 14 | # - NO BRACKETS 15 | # - NICELY FORMATTED CODE 16 | # - 4 SPACES 17 | 18 | # # INFINITE LOOP! 19 | # while 1: 20 | # pass 21 | 22 | ################################### 23 | 24 | # If statement 25 | 26 | a = 10 27 | 28 | print('a is an', end=' ') 29 | if a%2 == 0: 30 | print('even number!') 31 | else: 32 | print('odd number!') 33 | 34 | 35 | print('a is', end=' ') 36 | if a <= 10: 37 | print('less than or equal to 10') 38 | else: 39 | print('greater than 10') 40 | 41 | # Relational operators: 42 | # == equal to 43 | # != not equal to 44 | # < less than 45 | # > greater then 46 | # <= 47 | # >= 48 | 49 | # Bools 50 | print(8 == 8) # True 51 | print(1 == 2) # False 52 | print(True == 1) # True 53 | print(False == 0) # True 54 | 55 | # None, same as NULL in C 56 | var = None 57 | 58 | if var is None: 59 | print('The variable is None!') 60 | 61 | 62 | # Combining conditions - airplane edition 63 | age = 8 64 | 65 | if age < 2: 66 | print('Free fare!') 67 | 68 | elif (age > 2) and (age < 12): # We can have elifs as much as we want 69 | print('50% off!') 70 | 71 | else: 72 | print('Full fare!') 73 | 74 | # Logical operators: 75 | # not, and, or, ^ (xor) 76 | 77 | # Testing strings 78 | 79 | test_str = 'It is not safe to go alone. Take this!' 80 | 81 | if 'safe' in test_str: 82 | print('Thanks!') 83 | 84 | # The same thing works for lists (checking if 'y' is one of the elements of the list) 85 | 86 | test_list = [1, 2, 3.14, 4] 87 | 88 | if 3.14 in test_list: 89 | print(test_list) 90 | 91 | 92 | # List as condition 93 | test_list = [] 94 | 95 | if test_list: 96 | print('The list is not empty!') 97 | else: 98 | print('The list is empty!') 99 | 100 | ################################### 101 | 102 | # QUESTION 1 103 | # What does this code print out? 104 | 105 | x = 81 106 | 107 | if (x%2 == 0) and (x > 100): 108 | print('Alpha') 109 | 110 | elif ((x%3 == 0) and not (x%9 == 0)): 111 | print('Kilo') 112 | 113 | elif ((x%5 == 0) or ((x-1)/10 < 10)): 114 | print('Delta') 115 | 116 | else: 117 | print('Echo') 118 | 119 | ################################### 120 | 121 | # Controlling the flow of a loop - finding the first number divisible by 2, 3 and 7; breaking the loop after 122 | 123 | x = 1 124 | while x < 100: 125 | 126 | if (x%2 == 0) and (x%3 == 0) and (x%7 == 0): 127 | print(x) 128 | 129 | # Break the loop 130 | break 131 | 132 | x += 1 133 | 134 | 135 | # Controlling the flow of a loop - mark all leap years between 1990 and 2020 (continue statement) 136 | # Leap years: 137 | # - The year can be evenly divided by 4; 138 | # - If the year can be evenly divided by 100, it is NOT a leap year, unless; 139 | # - The year is also evenly divisible by 400. Then it is a leap year. 140 | 141 | x = 1990 142 | while x < 2020: 143 | 144 | x += 1 145 | 146 | if ((x%4 == 0) and (x%100 != 0)) or (x%400 == 0): 147 | print(x, 'leap') 148 | 149 | continue 150 | 151 | print(x) 152 | 153 | 154 | ################################### 155 | 156 | # For loop 157 | 158 | # First 50 numbers in the Fibonnaci series 159 | a, b = 0, 1 160 | for i in range(50): 161 | print(b, end=',') 162 | a, b = b, a+b 163 | 164 | 165 | # Looping over a predefined list 166 | cities = ['New York', 'Paris', 'Peckham'] 167 | for city in cities: 168 | print(city) 169 | 170 | 171 | # Enumerating 172 | for i, city in enumerate(cities): 173 | print(i, city) 174 | 175 | 176 | # Zipping lists 177 | countries = ['USA', 'France', 'UK'] 178 | for country, city in zip(countries, cities): 179 | print(country, city) 180 | 181 | 182 | ################################### 183 | 184 | # QUESTION 2 185 | # You are reading someone's spagetti code, and you see this little gem. What does it do? 186 | 187 | data = [ 188 | [65.90, 432.6], 189 | [12.40, 76.30], 190 | [12.45, 90.80], 191 | [57.40, 47.20]] 192 | 193 | var = 0 194 | for x, y in data: 195 | 196 | if x > y: 197 | var += x 198 | else: 199 | var += y 200 | 201 | print(var/len(data)) 202 | 203 | ################################### 204 | 205 | ### READING FILES 206 | 207 | file_name = 'data.txt' 208 | 209 | # Opening a file in reading mode 210 | with open(file_name, 'r') as f: 211 | 212 | # Print the file object status 213 | print(f.closed) 214 | 215 | # Print out one line from the file 216 | print(f.readline()) 217 | 218 | # File is closed now 219 | print(f.closed) 220 | 221 | # The file will close outside the with block - reading it produces an error! 222 | # print(f.readline()) 223 | 224 | # Reading the file line by line 225 | with open(file_name) as f: 226 | 227 | for line in f: 228 | print(line) 229 | 230 | 231 | # Manipulating strings - splitting a string by a delimiter 232 | a = 'one,two,three,four' 233 | print(a.split(',')) 234 | 235 | # Stripping whitespace 236 | b = ' center ' 237 | print(b.strip()) 238 | 239 | 240 | # Printing the file content as a list 241 | with open(file_name) as f: 242 | 243 | for line in f: 244 | 245 | # Remove newline char 246 | line = line.replace('\n', '') 247 | 248 | # Split the line into a list by a comma 249 | line = line.split(',') 250 | 251 | print(line) -------------------------------------------------------------------------------- /Lecture 5/L5_example_binary_files.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | 5 | def readBinary(file_name): 6 | """ Read a binary file. """ 7 | 8 | with open(file_name, 'rb') as fid: 9 | 10 | # Read the header 11 | header_size = int(np.fromfile(fid, dtype=np.uint32, count=1)) 12 | station_latitude = np.fromfile(fid, dtype=np.float64, count=1) 13 | station_longitude = np.fromfile(fid, dtype=np.float64, count=1) 14 | elevation = np.fromfile(fid, dtype=np.float64, count=1) 15 | station_name = (b''.join(np.fromfile(fid, dtype='c', count=100))).decode("utf-8") 16 | year = np.fromfile(fid, dtype=np.uint32, count=1) 17 | data_size = int(np.fromfile(fid, dtype=np.uint32, count=1)) 18 | 19 | # Skip to the end of the header 20 | fid.seek(header_size) 21 | 22 | # Read the tabular data 23 | table = np.fromfile(fid, dtype=np.float64, count=2*data_size) 24 | table = np.reshape(table, (data_size, 2)) 25 | 26 | 27 | # Print header data 28 | print(header_size) 29 | print(station_latitude) 30 | print(station_longitude) 31 | print(elevation) 32 | print(station_name) 33 | print(year) 34 | print(data_size) 35 | 36 | # Print the tabular data 37 | print(table) 38 | 39 | 40 | 41 | def writeBinary(file_name, header, data): 42 | """ Write a binary file. """ 43 | 44 | # Open a file for binary writing 45 | with open(file_name, 'wb') as fid: 46 | 47 | # Write the header 48 | for data_type, entry in header: 49 | 50 | # Make sure each entry is written in the proper file format 51 | np.array(entry).astype(data_type).tofile(fid) 52 | 53 | # Write tabular data 54 | data.tofile(fid) 55 | 56 | 57 | 58 | if __name__ == '__main__': 59 | 60 | ### READING AND WRITING BINARY FILES 61 | 62 | """ 63 | Binary files are files which are written in a way that is easily understandable to computers - in pure 64 | machine code. This of course makes them difficult for humans to read, but they do have advantages - they 65 | are usually very small in size and can be read by a computer extremely fast. 66 | 67 | To read a binary file, you alsolutely NEED to know its format. That means that you know what type are the 68 | data inside the file, and what is their order and size. 69 | 70 | Let me illustrate. Let's say we have a binary file which contains yearly temperature measurements from one 71 | weather station. Such binary file would have 2 parts: 72 | 1) The header - Header size in bytes, station's geographical coordinates, station name, 73 | year when the data was taken, number of temperature measurements in the table. 74 | 2) Data table - Table which contains the temperature data. The table has two columns: measurement time 75 | and temperature value 76 | 77 | HEADER: 78 | - header_size: [uint32] header size in bytes 79 | - station_latitude: [float64] station's latitude in degrees 80 | - station_longitude: [float64] station's longitude in degrees 81 | - elevation: [float64] elevation in meters 82 | - station_name: [100 characters] name of the weather station 83 | - year: [uint32] year when the data was collected 84 | - data_size: [uint32] number of measurements in the table 85 | 86 | DATA TABLE: 87 | Contains data_size number of measurements. Each measurement has this format: 88 | - jd_time: [float64] julian date when the measurement was taken 89 | - temperature: [float64] temperature value in deg. of Celsius 90 | 91 | 92 | IMPORTANT: 93 | What is important is that each entry has a specified data type. Also, an extermely good idea is that the 94 | first entry is the header size. Imagine that one day we add some entries to the header, because we need to 95 | expand out data format as we wanted include some more info. If the header size is not present, then the 96 | new format will not work will any old code we have. By using the header size, we can keep track of the 97 | number of bytes we have read in, and just skip all unread bytes until our tabular data begines. 98 | 99 | So, how do we calculate the header size? We take a look at individual size and number of data types, and 100 | sum them all together to get the number of bits. Then, we divide that number by 8 to get the size in 101 | bytes. In our case, we have: (32 + 64 + 64 + 64 + 100*8 + 32 + 32)/8 = 136 bytes in the header. Thus, 136 102 | is our header size. 103 | 104 | """ 105 | 106 | ### WRITING BINARY FILES 107 | 108 | # We will create fake temperature measurement data 109 | temp_measure = [] 110 | 111 | for jd in np.arange(2431456.5, 2431821.5, 1.0/6): 112 | temp_measure.append([jd, np.random.normal(13, 19)]) 113 | 114 | temp_measure = np.array(temp_measure) 115 | 116 | # We will define a header as a list of (data type, value) pairs, in the proper order. 117 | header = [[np.uint32, 136], 118 | [np.float64, 33.676971], 119 | [np.float64, -106.475330], 120 | [np.float64, 1500], 121 | [np.str_, '{:100s}'.format('Trinity Site')], 122 | [np.uint32, 1945], 123 | [np.uint32, temp_measure.shape[0]]] 124 | 125 | # Name of our data file 126 | file_name = 'Trinity_site_1945_temps.bin' 127 | 128 | # Writing the binary file 129 | writeBinary(file_name, header, temp_measure) 130 | 131 | """ 132 | If you were to open this file, you would see just a bunch of jiberish. To retrieve the file contents, 133 | we need to read it by knowing its format. 134 | """ 135 | 136 | # Read the binary file 137 | readBinary(file_name) -------------------------------------------------------------------------------- /Lecture 7/L7_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import sys 4 | 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | 8 | from TextFileParser import parseTextFile 9 | 10 | ################################### 11 | 12 | # INTEGRATING ODEs 13 | # Source for this example: http://www.danham.me/r/2015/10/29/differential-eq.html 14 | 15 | # We have a system of ordinary differential equations discribing the predator-prey system, i.e. how the 16 | # number of predators in an ecosystem changes over time in respect to the number of prey in the system, 17 | # and vice versa. 18 | 19 | # The equations describing the behaviour are: 20 | # dx/dt = x(2 - y - x) 21 | # dy/dt = -y(1 - 1.5x) 22 | # where x is the number of prey, y is the number of predators 23 | 24 | import scipy.integrate 25 | 26 | def ecosystem(state, t): 27 | """ The predator-prey system described with ODEs. """ 28 | 29 | # Unpack current values of x and y 30 | x, y = state 31 | 32 | # Differential equations 33 | d_x = x*(2 - y - x) 34 | d_y = -y*(1 - 1.5*x) 35 | 36 | return [d_x, d_y] 37 | 38 | 39 | # We define the initial conditions (1 prey and 1 predator) 40 | init_state = [5, 1] 41 | 42 | # The range of times to investigate (time step = 0.01) 43 | t_array = np.arange(0, 20, 0.01) 44 | 45 | # Perform the integration 46 | results = scipy.integrate.odeint(ecosystem, init_state, t_array) 47 | 48 | # Print states in all timesteps 49 | print(results) 50 | 51 | # Show the results 52 | results = np.array(results) 53 | plt.plot(results[:,0], results[:,1]) 54 | plt.xlabel('Prey') 55 | plt.ylabel('Predators') 56 | 57 | plt.show() 58 | 59 | 60 | ################################### 61 | 62 | # SPLINE INTERPOLATION 63 | 64 | import scipy.interpolate 65 | 66 | file_name = 'spline_data.csv' 67 | 68 | # Import spline data 69 | data = parseTextFile(file_name) 70 | data = np.array(data).astype(np.float64) 71 | 72 | # Unpack x and y 73 | x, y = data.T 74 | 75 | # Plot spline data 76 | plt.scatter(x, y) 77 | plt.show() 78 | 79 | # Perform spline interpolation 80 | spline = scipy.interpolate.CubicSpline(x, y) 81 | 82 | # Draw samples from the interpolated model 83 | x_spline = np.linspace(np.min(x), np.max(x), 1000) 84 | y_spline = spline(x_spline) 85 | 86 | # Plot the interpolated results 87 | plt.scatter(x, y) 88 | plt.plot(x_spline, y_spline) 89 | 90 | plt.show() 91 | 92 | 93 | 94 | ################################### 95 | 96 | import scipy.fftpack 97 | import scipy.signal 98 | 99 | 100 | 101 | # TIME SERIES DATA - FOURIER TRANSFORM 102 | 103 | # Data file 104 | data_file_name = 'radiometer_data.csv' 105 | 106 | # Load data from the text file 107 | data = parseTextFile(data_file_name) 108 | data = np.array(data).astype(np.float64) 109 | 110 | # Split the data into time and signal 111 | time_data, signal_data = data.T 112 | 113 | # Show data 114 | plt.plot(time_data, signal_data) 115 | plt.show() 116 | 117 | # Detrend the data (making sure the low frequency component is small) 118 | signal_data = signal_data - np.mean(signal_data) 119 | 120 | # Calculate samples per second 121 | sps = len(signal_data)/(np.max(time_data) - np.min(time_data)) 122 | 123 | 124 | ### Time series data plots 125 | 126 | # Plot power spectral density 127 | plt.psd(signal_data, Fs=sps) 128 | plt.show() 129 | 130 | # Plot the spectrogram 131 | plt.specgram(signal_data, Fs=sps, cmap='inferno') 132 | plt.show() 133 | 134 | ### 135 | 136 | 137 | # Number of samplepoints 138 | N = len(signal_data) 139 | 140 | # Temporal spacing of samples 141 | T = 1.0/sps 142 | 143 | # Run FFT 144 | yf = scipy.fftpack.fft(signal_data) 145 | 146 | # Make the range of frequencies 147 | xf = np.linspace(0.0, 1.0/(2.0*T), N/2) 148 | 149 | # Calculate the amplitude 150 | fft_amplitude = 2.0/N*np.abs(yf[0:N//2]) 151 | 152 | plt.plot(xf, fft_amplitude) 153 | plt.show() 154 | 155 | 156 | ### Filtering our signal data with a Butterworth low-pass filter 157 | 158 | # Filter order 159 | N = 2 160 | 161 | # Cutoff frequency, Wn is given normalized to the Nyquist frequency (sps/2) 162 | Wn = 10.0/(sps/2) 163 | 164 | # Make a Butterworth filter 165 | B, A = scipy.signal.butter(N, Wn) 166 | 167 | 168 | ### Show signal response - (lecture note: C/P, DO NOT GO IN DETAIL) 169 | w, h = scipy.signal.freqz(B, A, len(xf)) 170 | plt.semilogx((xf*0.5/np.pi)*w, 20*np.log10(np.abs(h))) 171 | plt.title('Butterworth filter frequency response') 172 | plt.xlabel('Frequency [Hz]') 173 | plt.ylabel('Amplitude [dB]') 174 | plt.margins(0, 0.1) 175 | plt.grid(which='both', axis='both') 176 | plt.axvline(Wn*sps/2, color='green') # cutoff frequency 177 | plt.show() 178 | 179 | 180 | ### 181 | 182 | # Apply the filter to our data 183 | signal_data_filtered = scipy.signal.filtfilt(B, A, signal_data) 184 | 185 | # Plot both the original and the filtered data 186 | plt.plot(time_data, signal_data) 187 | plt.plot(time_data, signal_data_filtered, linewidth=5) 188 | plt.show() 189 | 190 | 191 | ################################### 192 | 193 | # WAVELETS 194 | 195 | # Let us try to detect the position and the duration of the event in the time series 196 | 197 | # Define the widths for wavelets ('probe sizes'): from 0 to 2 seconds, 0.1 second resolution 198 | resolution = 0.1 199 | widths = np.arange(0.1, 2, resolution)*sps 200 | 201 | # Run the wavelet transform 202 | cwtmatr = scipy.signal.cwt(signal_data_filtered, scipy.signal.ricker, widths) 203 | 204 | # Find the maximum peaks in the wavelet parameter space 205 | max_peak = np.unravel_index(cwtmatr.argmax(), cwtmatr.shape) 206 | max_width = widths[max_peak[0]]/sps 207 | max_time = max_peak[1]/sps 208 | 209 | print('Peak at:', max_time, 'Duration:', max_width) 210 | 211 | plt.imshow(cwtmatr, cmap='inferno', aspect='auto') 212 | plt.show() -------------------------------------------------------------------------------- /Lecture 6/Homework 1/L6_homework_text.txt: -------------------------------------------------------------------------------- 1 | Calculating the age of the Universe 2 | --------------------------------- 3 | 4 | Back in the 1920s, the world was shocked by Edwin Hubble's revelation that the universe is expanding. While his discovery was groundbreaking back in the day, we can easily replicate his results today. Let me give you a quick overview of the background, so you know what the task actually is (astronomers, excuse me if I oversimplified the underlying physics for the needs or this homework). 5 | 6 | It all boils down to one thing - the Doppler effect. For those who need reminding: you can notice the Doppler effect while you are standing on the side of the road while an ambulance is racing towards the hospital. First, as it comes towards you, you can hear that its siren has a high pitch (its sound frequency is higher). At the moment it passes next to you, you can hear a sudden drop in its pitch, as if the sound it produces becomes deeper (the frequency of the sound becomes lower). The reason for this is simple: The speed of sound in air is (more or less) constant, at around 343 m/s - if you put a siren on a car, go to the highway and floor it, once you reach a speed of 110 km/h (~30 m/s), the sound waves produced by the siren in front of your car will NOT travel at 373 m/s. Instead, the sound waves in front of your car will still travel at 343 m/s, but they will become "compressed", effectively shortening the wavelength, thus increasing the frequency of the sound waves. At the back of your car, the opposite happens - the sound waves are getting 'stretched'. 7 | The same things happens with light. Its speed is fixed at about 3*10^8 m/s, so if you start going fast enough, the frequency of light you are emitting will start to change. The light "waves" in front of you will get "compressed", and this will shorten its wavelength. And light with shorter wavelengths will seem bluer than normal light. This is called "blue shift". If someone was looking at you from behind, as you are moving away from them, they would see the opposite - as the wavelength of light gets stretched, you would seem redder than you are. This effect is called "red shift". 8 | 9 | If you're still confused, maybe Carl Sagan can help you out: https://www.youtube.com/watch?v=Uy7rrrCQh2w 10 | 11 | So, back to the main story. Edwin Hubble looked at light of distant galaxies, and concluded that they are all red shifted, i.e. moving away from us. Furthermore, galaxies further away from us seemed to be moving away at a greater speed than those which are closer. These observations are called "Hubble's law". The best explanation for them is that our Universe is expanding. This opens up another question - if the Universe is expanding, when did this expansion begin? 12 | 13 | We will look into how we can calculate that time, but first we need some data. There are many online databases one can access and download information about galaxies. What we are looking for are 2 pieces of information - the distance to the galaxy, and its relative speed by which it is moving away from us. For convenience, I have provided you with a CSV file (you can read it in as any text file with comma-delimited values) which contains this data. The first column is the name of the galaxy, the second column is the distance in megaparsecs, and the third column is its relative radial velocity (in km/s). 14 | 15 | If you plot this data, velocity vs. distance (please to that), you can notice a linear dependence between them. This gives us a great opportunity to test out our newly acquired linear regression skills. Use any linear regression technique you like, although I recommend using one of the robust techniques, as there are many outliers. But first, convert the distance from megaparsecs to kilometers (see 'Steps for solving the homework' below for more information). 16 | 17 | OK, so after we have fitted the line, we have a relation which is telling us how the distance to the galaxy changes with its relative velocity. Congratulations! You have now measured the Hubble constant! (although not in the units astronomers usually use). The question now is: at what time was the velocity of all galaxies zero, i.e. how long before the present did the expansion start? So, what can the parameters of the line tell us about this? What are the units of the slope and the intercept? 18 | 19 | The slope itself is the Hubble constant, and its units are (km/s)/km = 1/s. The inverse of the Hubble constant is called Hubble time, and is very close to the actual age of the Universe ("The Hubble time is the age it would have had if the expansion had been linear, and it is different from the real age of the universe because the expansion isn't linear; they are related by a dimensionless factor which depends on the mass-energy content of the universe, which is around 0.96 in the standard Lambda-CDM model." - Wikipedia article on Hubble's law). So if we take the inverse of our slope and convert the obtained value from seconds to years, multipy that by 0.96, we will get the approximate age of the universe! 20 | 21 | 22 | 23 | Steps for solving the homework: 24 | 1. Load the galaxy data from the galaxies.csv file. 25 | 2. Convert megaparsecs to kilometers: 26 | 1 MPc = 3.08567758149137e+19 km 27 | 3. Plot the velocity vs. distance graph. 28 | 4. Fit a line through the velocity vs. distance data. 29 | 5. Take the inverse of the slope, convert the value from seconds to years, and multipy it by 0.96 30 | 6. Print out the result in billions of years. 31 | 7. Compare the obtained value with the currently accepted age of the Universe. 32 | 33 | BOUNS: 34 | What if we used a different method for linear regression? How does this influence the final result? Should we be careful when using the least squares regression on noisy data? NOTE: Be aware that you can't get the line parameters directly from RANSAC and Theil-Sen, you will have to feed them two X values (e.g. 0 and 1e22), get the Y values using y = estimator.predict(x), and calculate the slopes yourself. Also, RANSAC will randomly choose a very small subset of points from the data to make an estimate - you can make that subset larger by passing the min_points argument to the RANSAC estimator, e.g. if you want to use 50% points, you would write: ransac = RANSACRegressor(min_samples=0.5). 35 | 36 | This homework was inspired by this Reddit post (although the guy fudged the data a bit): 37 | https://www.reddit.com/r/dataisbeautiful/comments/5st2qn/i_got_a_dataset_of_4240_galaxies_and_calculated/ -------------------------------------------------------------------------------- /Lecture 3/L3_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | 4 | ### READING FILES 5 | 6 | file_name = 'data.txt' 7 | 8 | 9 | # Reading in and parsing file contents 10 | data_list = [] 11 | with open(file_name) as f: 12 | 13 | # SKip the header (the first line) 14 | next(f) 15 | 16 | for line in f: 17 | 18 | # Remove newline char 19 | line = line.replace('\n', '') 20 | 21 | # Split the line into a list by a comma 22 | line = line.split(',') 23 | 24 | # Parse the line 25 | num = line[0] 26 | name = line[1].strip() 27 | epoch = int(line[2]) 28 | elements = list(map(float, line[3:9])) 29 | ref = line[9] 30 | 31 | # Add the line to the data list 32 | data_list.append([num, name, epoch, elements, ref]) 33 | 34 | print(num, name, epoch, elements, ref) 35 | 36 | 37 | ################################### 38 | 39 | print(data_list) 40 | 41 | # Wile E. Coyote rewrites history... 42 | for line in data_list: 43 | line[1] = 'Coyote' 44 | 45 | print(data_list) 46 | 47 | # But before we write the data back to disk... 48 | ################################### 49 | 50 | ### STRING FORMATTING 51 | 52 | ### Note for the lecture: 53 | ### C/P and explain how formatting works 54 | 55 | # Converting floats to strings 56 | x = 3.14159 57 | 58 | print('{:4.2f}'.format(x)) 59 | 60 | # Signed formatting 61 | print('{:+5.2f}'.format(x)) 62 | 63 | # Zero padding 64 | print('{:06.2f}'.format(x)) 65 | 66 | # More decimals 67 | print('{:7.5f}'.format(x)) 68 | 69 | 70 | 71 | # More decimal places than the number precision 72 | y = 2.71 73 | print('{:7.5f}'.format(y)) 74 | 75 | # Less decimal precision, but same size -> left padding 76 | print('{:7.2f}'.format(y)) 77 | 78 | 79 | 80 | # Integers (same singed and zero padding rules) 81 | z = 42 82 | print('{:7d}'.format(z)) 83 | 84 | # Strings 85 | print('{:10}'.format('wile e')) 86 | 87 | # Align to the right 88 | print('{:>10}'.format('wile e')) 89 | 90 | # Named agruments 91 | print("{a} {b} {c}".format(a=5, b=8, c=10)) 92 | 93 | ################################### 94 | 95 | ### WRITING FILES 96 | 97 | # Writing the data back to the list 98 | new_file_name = 'true_data.txt' 99 | 100 | # Open a file for writing (if a file with the same name exists, it will erase its content!) 101 | with open(new_file_name, 'w') as f: 102 | 103 | # Write the header 104 | f.write('Num,Name,Epoch,q,e,i,w,Node,Tp,Ref\n') 105 | 106 | for line in data_list: 107 | 108 | # Composing a string 109 | str_line = ['{:>3}'.format(line[0]), line[1], '{:5d}'.format(line[2])] 110 | 111 | # Convert all elemets using the same format 112 | for element in line[3]: 113 | str_line.append('{:.3f}'.format(element)) 114 | 115 | # Add the reference 116 | str_line.append(line[-1]) 117 | 118 | print(str_line) 119 | 120 | # Convert the list to a comma delimited string 121 | final_line = ','.join(str_line) 122 | 123 | # Write the line 124 | f.write(final_line+'\n') 125 | 126 | ################################### 127 | 128 | # Appending to a file 129 | with open(new_file_name, 'a') as f: 130 | 131 | f.write('Wile E. was here') 132 | 133 | ################################### 134 | 135 | ### PYTHON MODULES 136 | 137 | # Python standard library: https://docs.python.org/3/library/ 138 | 139 | import math 140 | 141 | # Sqrt 142 | print(math.sqrt(2)) 143 | 144 | # Sine 145 | print(math.sin(math.pi)) 146 | 147 | # Log10 148 | print(math.log10(100)) 149 | 150 | # Random module 151 | import random 152 | 153 | # Random integer in the 1 to 100 range 154 | print(random.randint(1, 100)) 155 | 156 | # Random float in the 0 to 1 range 157 | print(random.random()) 158 | 159 | # Shuffle a list 160 | a = [1, 2, 3, 4, 5] 161 | random.shuffle(a) 162 | print(a) 163 | 164 | # Sample 10 elements from a list 165 | b = range(1, 100) 166 | print(random.sample(b, 10)) 167 | 168 | # Sampling a gaussian distribution 169 | for i in range(10): 170 | print(random.gauss(0, 2)) 171 | 172 | 173 | ################################### 174 | ### Ways of importing modules 175 | 176 | # Module alias 177 | import math as m 178 | 179 | print(m.sqrt(2)) 180 | 181 | # Importing individual functions - PREFERED! 182 | from math import sqrt 183 | 184 | print(sqrt(2)) 185 | 186 | # Importing all functions from a module - NOT RECOMMENDED! 187 | from math import * 188 | 189 | print(sqrt(2)) 190 | print(pi) 191 | 192 | 193 | ################################### 194 | # FILE HANDLING - os library 195 | 196 | import os 197 | 198 | # Listing the contents of the current directory 199 | print(os.listdir('.')) 200 | 201 | # Printing the current directory 202 | print(os.getcwd()) 203 | 204 | # Changing the current directory one up 205 | os.chdir('..') 206 | print(os.getcwd()) 207 | 208 | # Directory separator 209 | # DO NOT USE / or \ 210 | print(os.sep) 211 | 212 | ### Making a new directory 213 | 214 | # Construct a new path to the directory 215 | new_dir_path = os.path.join(os.getcwd(), 'test') 216 | print(new_dir_path) 217 | 218 | # Make new dir if the dir does not exist 219 | if not os.path.exists(new_dir_path): 220 | os.mkdir(new_dir_path) 221 | else: 222 | print('The directory already exists!') 223 | 224 | ### 225 | 226 | # Make an example file in the new directory 227 | file_name = 'top_secret.txt' 228 | file_path = os.path.join(new_dir_path, file_name) 229 | with open(file_path, 'w') as f: 230 | pass 231 | 232 | # Delete the file 233 | if os.path.isfile(file_path): 234 | os.remove(file_path) 235 | else: 236 | print('The file does not exist!') 237 | 238 | ################################### 239 | 240 | # FILE HANDLING - shutil library 241 | 242 | import shutil 243 | 244 | 245 | # Make an example file 246 | with open(file_path, 'w') as f: 247 | pass 248 | 249 | # Copying files 250 | copy_path = 'unclassified.txt' 251 | shutil.copy2(file_path, copy_path) 252 | 253 | # Moving/renaming files 254 | new_name = 'public_release.txt' 255 | shutil.move(copy_path, new_name) -------------------------------------------------------------------------------- /Lecture 1/L1_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | # This is a comment 4 | 5 | print('Hello world!') 6 | 7 | a = 15.9 8 | b = 6 9 | 10 | print('a =', a) 11 | print('b =', b) 12 | 13 | print('Addition:', a + b) 14 | print('Subtraction:', a - b) 15 | print('Multiplication:', a*b) 16 | print('Division:', a/b) 17 | 18 | print('Integer division:', a//b) 19 | print('Modulo:', a%b) 20 | 21 | print('a squared:', a**2) 22 | print('a cubed:', a**3) 23 | print('a^9:', a**9) 24 | 25 | print('sqrt(2):', 2**(1.0/2)) 26 | 27 | c = round(1.1/3, 4) 28 | print('Rounding floats:', c) 29 | 30 | ################################### 31 | 32 | # Swapping variable values 33 | print('Before: a = ', a, ', b =', b) 34 | 35 | a, b = b, a 36 | 37 | print('After: a = ', a, ', b =', b) 38 | 39 | 40 | ################################### 41 | 42 | # Strings 43 | # More on strings: https://www.tutorialspoint.com/python/python_strings.htm 44 | 45 | name = 'Monty Python' # Apostropes 46 | title = "Holy Grail" # Quotes 47 | 48 | print(name) 49 | print(title) 50 | 51 | full_title = name + ' and the ' + title 52 | print(full_title) 53 | 54 | # First char 55 | print(name[0]) 56 | 57 | # Last char 58 | print(name[-1]) 59 | 60 | # Ranges of chars 61 | print(name[0:5]) 62 | print(name[6:]) 63 | print(title[:4]) 64 | 65 | # Every other char 66 | print(name[::2]) 67 | print(name[1::2]) 68 | print(name[1:8:2]) 69 | 70 | # Modifying strings, Holy Grain 71 | print(title[:-1] + 'n') 72 | 73 | # String length 74 | print('Length:', len(name)) 75 | 76 | # New line escape char 77 | print('First line\nSecond line') 78 | 79 | # Repetition 80 | print('Hello! '*10) 81 | 82 | # Counting substrings 83 | print("'n's in " + name + ':', name.count('n')) 84 | 85 | # Replacing substrings 86 | print(name.replace('o', 'a')) 87 | 88 | ################################### 89 | 90 | # Type conversions 91 | x = "3.14" 92 | y = float(x) 93 | z = int(y) 94 | 95 | print(x, y, z) 96 | 97 | ################################### 98 | 99 | # QUESTION 1 100 | # What will this code print out: 101 | 102 | a = 'rosemead' 103 | b = 'new york' 104 | print(a[2:5] + b[4:] + 'a') 105 | 106 | # Semyorka was the world's first ICMB (developed in 1957), and it was used (in a modified form) to launch 107 | # Sputnik, the world's first artificial satellite. 108 | 109 | ################################### 110 | 111 | # Lists - ultimate containters! 112 | apollo_moon = [11, 12, 14, 15, 16, 17] 113 | 114 | print(apollo_moon) 115 | 116 | apollo_commanders = ['Armstrong', 'Conrad', 'Shephard', 'Scott', 'Young', 'Cernan'] 117 | 118 | print(apollo_commanders) 119 | 120 | # Accessing elements - same as strings! 121 | print('Post Apollo 13 commanders:', apollo_commanders[2:]) 122 | 123 | # List are MUTABLE - possible to change elements in-place 124 | apollo_commanders[0] = 'Lovell' 125 | 126 | print('If Neil got shingles before the flight:', apollo_commanders) 127 | 128 | # Appending an entry 129 | apollo_moon.append(18) 130 | apollo_moon.append(19) 131 | 132 | print('Added cancelled:', apollo_moon) 133 | 134 | # Popping the last entry 135 | cancelled = apollo_moon.pop() 136 | 137 | print(cancelled) 138 | print(apollo_moon) 139 | 140 | # Insert the failed Apolo 13 141 | apollo_moon.insert(2, 13) 142 | 143 | print(apollo_moon) 144 | 145 | # Remove by entry 146 | apollo_moon.remove(13) 147 | 148 | print(apollo_moon) 149 | 150 | # Remove by index 151 | apollo_moon.pop(-1) 152 | 153 | # Find the index of the entry 154 | print('Index of Apollo 11:', apollo_moon.index(11)) 155 | 156 | # Reversing a list (in place) 157 | apollo_moon.reverse() 158 | print('Reverse:', apollo_moon) 159 | 160 | # Reversing a list without modifying it 161 | print('Back to normal:', apollo_moon[::-1]) 162 | 163 | # Unpacking list elements to individual variables 164 | apollo12_crew = ['Pete', 'Dick', 'Al'] 165 | commander, cm_pilot, lm_pilot = apollo12_crew 166 | 167 | print(commander, cm_pilot, lm_pilot) 168 | 169 | # 2D list 170 | list2d = [[1, 2], [3, 4]] 171 | 172 | print(list2d) 173 | 174 | # Accessing elements of multidimensional lists 175 | print(list2d[0][1]) 176 | 177 | # Weird lists 178 | mission_details = [1969, 'Apollo 12', ['Conrad', 'Gordon', 'Bean'], 10.19194] 179 | 180 | print(mission_details) 181 | 182 | # QUESTION: Acessing Al Bean? 183 | print(lm_pilot, mission_details[2][2]) 184 | 185 | 186 | ww2 = [1939, 1940, 1941, 1942, 1943, 1944, 1945] 187 | 188 | # Appying a single function across a list 189 | ww2 = list(map(str, ww2)) 190 | print(ww2) 191 | 192 | # Joining list elements by a delimiter 193 | print(', '.join(ww2)) 194 | 195 | ################################### 196 | 197 | # QUESTION 2 198 | # What will this code print out: 199 | 200 | a = [1, 2, 3, 4] 201 | b = ['10', '11', '12', '13'] 202 | print(int("5" + b[-1]) + a[1]) 203 | 204 | # Answer: 515 205 | 206 | 207 | ################################### 208 | # Other list operations 209 | 210 | x = [1, 2, 20, 21, 22, 23, 1, 1] 211 | 212 | # Summing elements inside list 213 | print(sum(x)) 214 | 215 | # Max 216 | print(max(x)) 217 | 218 | # Min 219 | print(min(x)) 220 | 221 | # Counting occurences or an element in a list 222 | print(x.count(1)) 223 | 224 | # Sorting a list (IN PLACE SORTING) 225 | x.sort() 226 | print(x) 227 | 228 | # Reversing a list (IN PLACE) 229 | x.reverse() 230 | print(x) 231 | 232 | ################################### 233 | 234 | # Lists: shallow copy VS deep copy 235 | 236 | a = [1, 2, 3, 4] 237 | 238 | # Now let's point 'b' to 'a' 239 | b = a 240 | 241 | # We can see that 'b' is the same as 'a' 242 | print('a and b:', a, b) 243 | 244 | # Now if we change 'a' 245 | a[0] = 100 246 | print('a and b:', a, b) # 'b' changes as well! 247 | 248 | # This happens because 'b' was only pointing to 'a', the whole list was not copied 249 | # To copy the list completely, we use this 250 | b = list(a) 251 | 252 | # Now if we change 'a' again 253 | a[0] = 0 254 | 255 | # 'b' stays the same 256 | print('a and b:', a, b) 257 | 258 | 259 | ################################### 260 | 261 | # FOR THOSE WHO WANT TO KNOW MORE 262 | 263 | # Complex numbers 264 | 265 | r = 1 + 1j 266 | q = 5 - 3j 267 | 268 | print('r =', r) 269 | print('q =', q) 270 | print('r+q =', r + q) 271 | print('abs(r)', abs(r)) 272 | 273 | # Accessing complex number parts 274 | print('Re(q) = ', q.real) 275 | print('Im(q) =', q.imag) 276 | 277 | print('q conjg = ', q.conjugate()) 278 | print('q**2 =', q**2) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UWO Physics & Astronomy Python Course 2 | Python course designed for grad students of the Physics & Astronomy department at the University of Western Ontario. 3 | 4 | ## Course overview 5 | 6 | ### Lecture 1 7 | * Print statement (Hello world!) → running with Ctrl+B 8 | * Comments 9 | * Variables and types (duck typing): 10 | * ints, floats (+, -, *, /, //, **, %, round function) 11 | * Multiple assignments 12 | * Strings 13 | * Adding strings 14 | * Accessing individual chars 15 | * Selecting a range of chars 16 | * Modifying strings - immutable! 17 | * len function 18 | * Repetition by multiplying 19 | * Converting one type to another 20 | * Lists - universal containers! 21 | * append, pop, indexing, insert, remove 22 | * Reversing a list 23 | * Unpacking elements to variables 24 | * Multidimensional lists 25 | * Nested lists 26 | * range statement 27 | * map statement 28 | * join to string 29 | 30 | ### Lecture 2 31 | * While loops 32 | * If statements 33 | * For loops 34 | * File input/output 35 | * Hands on example 36 | * Homework - lists, loops 37 | 38 | ### Lecture 3 39 | * File I/O continued 40 | * os and shutil libraries - handling files, directories 41 | * Task - batch file renaming (inspired by a problem from real life) 42 | * Homework - batch file sorting 43 | 44 | ### Lecture 4 45 | * Functions 46 | * Programming style and style guides 47 | * Handling and visualizing data 1 48 | * Numpy ("numerical Python") 49 | * Matplotlib - basics plots 50 | * Task 1 - function for loading data from text files 51 | * Task 2 - load data from a file and plot it 52 | * Homework - handling time 53 | 54 | ### Lecture 5 55 | * Advanced plotting in matplotlib 56 | * Advanced data manipulation in numpy 57 | * Sampling univariate and multivariate probability distributions 58 | * Plotting histograms 59 | * Sorting arrays 60 | * Plotting images 61 | * Plotting higher dimensionality data 62 | 63 | ### Lecture 6 64 | * linear regression with least squares, RANSAC and Theil-Sen estimator (and their comparison) 65 | * minimization: BFGS, Nelder-Mead and basin-hopping 66 | * fitting nonlinear models 67 | 68 | ### Lecture 7 69 | * integrating ODEs 70 | * spline interpolation 71 | * Fourier transform 72 | * filters for signal processing 73 | * wavelets 74 | * detecting a low-frequency signal (determine it's exact occurrence in time and duration) in data abundant with high-frequency noise 75 | 76 | ### Lecture 8 77 | * generators 78 | * object oriented programming (basics, operator overloading, inheritance) 79 | * list comprehension 80 | * dictionaries 81 | * sets 82 | 83 | ### Lecture 9 84 | * parallel programming (multiprocessing, pool of workers, universal function for parallelization) 85 | * exceptions (i.e. error handling) 86 | * decorators 87 | * regular expressions 88 | 89 | ### Lecture 10 90 | * astroquery - querying astronomical databases and plotting data (we will have fun with SDSS data and color magnitude diagrams of globular clusters) 91 | * a short word on dependencies 92 | * Cython - converting slow Python code to C 93 | 94 | ### Homeworks 95 | Homework solutions are available upon request. 96 | 97 | 98 | ## Installing Python on your computer 99 | 100 | There are two approaches to installing Python packages: 101 | 102 | ### a) Installing Python Anaconda 103 | 104 | Python Anaconda is a software package which contains Python + most used Python libraries. In all probability, is has all libraries you may ever need. To install Python Anaconda go here: https://www.anaconda.com/download and be careful to select the Python 3 version. 105 | If you are using Sublime on Linux, you might have to change an environment variable which will tell Sublime which installation of Python to use. Please see the section “Installing Sublime on your computer” below which will explain how to install Sublime Text 3 as well. 106 | 107 | ### b) Installing everything from scratch 108 | 109 | This is probably a more difficult route, but this will give you a greater knowledge and flexibility when it comes to managing your Python libraries. This is what I always do, as I like to have a better control over my Python installation. 110 | The thing is, I cannot give universal instructions how to install Python and all its packages on your computer, as it depends on the operating system you are using. What I can do is give you a list of things you’ll need, and let you google how to install them and get them working – there are plenty of resources online. 111 | We are using Python 3.x (I recommend installing Python with the highest ‘x’ you can find, i.e. the latest version). 112 | 113 | 1. Python 3 114 | 115 | 1. Python libraries: 116 | 1. Numpy (when installing on Windows, be sure to get the MKL package as well) 117 | 1. Matplotlib 118 | 1. Scipy (if the installation is failing on Linux, you are probably missing a few dependencies) 119 | 1. Sci-kit learn 120 | 1. Jupyter notebook 121 | 1. Cython (this one may be tricky to get working on Windows if you don’t have Visual Studio installed, you will have to install an external C compiler and play with a few configuration files, but there are instructions online how to get it to work) 122 | 1. astroquery 123 | 124 | 125 | 126 | ## Installing Sublime on your computer 127 | If you want to install Sublime as well, go to this website: https://www.sublimetext.com/3 download and install it. 128 | 129 | To install Sublime Anaconda, which will make your life easier, do this: 130 | 131 | 1. Open Sublime 132 | 2. Go to Tools->Command Palette 133 | 3. A new text box will open, start writing inside "Install Package Control", when that appears as an option, press Enter and it will install Package Control, i.e. the functionality to add more external packages. 134 | 4. Go to Tools->Command Palette again, start typing "Install Package" and press Enter when "Package Control: Install package" appears. 135 | 5. You will be presented with a list of packages to install. 136 | 6. Write "Anaconda" in the text box and install the first package on the list - Anaconda for Python. 137 | 138 | Once this package is installed, Sublime will have Python auto-completion, code linting, etc. It will also try to tell you that you need to write your code according to the default Python coding style known as PEP8, as it will surround all code which does not conform to that standard in white boxes. This can be a bit annoying, and can be disabled if you do the following: 139 | 140 | 1. Open Sublime and go to ```Preferences -> Package settings -> Anaconda -> Settings - User``` (here: https://s27.postimg.org/). An empty file called "Anaconda.sublime-settings" will open. 141 | 2. Write inside it: 142 | ``` 143 | { 144 | "pep8": false, 145 | } 146 | ``` 147 | 3. Save the file. This will remove the white boxes. 148 | -------------------------------------------------------------------------------- /Lecture 4/L4_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | 4 | # FUNCTIONS 5 | 6 | def addNums(a, b): 7 | """ Adds two numbers and returns the result. """ 8 | 9 | c = a + b 10 | 11 | return c 12 | 13 | # Calling a function 14 | print(addNums(5, 8)) 15 | 16 | 17 | # Input for the decimalHour function: 05:29:45 18 | 19 | def decimalHour(time_string): 20 | """ Converts time from the 24hrs hh:mm:ss format to HH.hhh format. """ 21 | 22 | hh, mm, ss = time_string.split(':') 23 | 24 | hh, mm, ss = map(float, (hh, mm, ss)) 25 | 26 | result = ((ss/60) + mm)/60 + hh 27 | 28 | return result 29 | 30 | 31 | # Call the function 32 | print(decimalHour('05:29:45')) 33 | 34 | # Call a function without the arguments 35 | print(decimalHour) 36 | 37 | 38 | def UTCdecimal(time_string, timezone=0): 39 | """ Converts time from the 24hrs hh:mm:ss to decimal hours, with respect to the timezone. """ 40 | 41 | # Get the decimal time 42 | result = decimalHour(time_string) + timezone 43 | 44 | # Return the time (handle midnight wraparound) 45 | return result % 24 46 | 47 | 48 | # Calling without parameters (time of the Trinity test on July 16, 1945) 49 | print(UTCdecimal('05:29:45')) 50 | 51 | # Calling with the timezone parameter (time in London, UK at the time of the Trinity test) 52 | print(UTCdecimal('05:29:45', timezone=-7)) 53 | 54 | 55 | ### Local and global variables 56 | 57 | # Global vs local variables 58 | 59 | def test(var): 60 | print('Input:', var) 61 | 62 | # This change will not influence the value of 'var' outside the function! 63 | var = 20 64 | 65 | print('Changed:', var) 66 | 67 | return 0 68 | 69 | var = 10 70 | 71 | test(var) 72 | 73 | print('Global:', var) 74 | 75 | 76 | ### Unpacking function arguments from a list 77 | 78 | def isTriangle(a, b, c): 79 | """ Checks if the given triangle sides can form a triangle. """ 80 | 81 | if c > (a+b): 82 | return False 83 | 84 | elif b > (a+c): 85 | return False 86 | 87 | elif a > (b+c): 88 | return False 89 | 90 | return True 91 | 92 | triangle_sides = [2, 3, 4] 93 | 94 | # Unpacking list as function arguments with * (star) 95 | print('Is ', triangle_sides, 'a triangle:', isTriangle(*triangle_sides)) 96 | 97 | ################################### 98 | 99 | ### DISCUSSION BREAK 100 | 101 | # Python philosophy 102 | # PEP20: The Zen of Python 103 | """ 104 | >> import this 105 | Beautiful is better than ugly. 106 | Explicit is better than implicit. 107 | Simple is better than complex. 108 | Complex is better than complicated. 109 | Flat is better than nested. 110 | Sparse is better than dense. 111 | Readability counts. 112 | Special cases aren't special enough to break the rules. 113 | Although practicality beats purity. 114 | Errors should never pass silently. 115 | Unless explicitly silenced. 116 | In the face of ambiguity, refuse the temptation to guess. 117 | There should be one-- and preferably only one --obvious way to do it. 118 | Although that way may not be obvious at first unless you're Dutch. 119 | Now is better than never. 120 | Although never is often better than *right* now. 121 | If the implementation is hard to explain, it's a bad idea. 122 | If the implementation is easy to explain, it may be a good idea. 123 | Namespaces are one honking great idea -- let's do more of those! 124 | """ 125 | 126 | 127 | # Style guide we are using: https://developer.lsst.io/coding/python_style_guide.html 128 | 129 | # How to name things: 130 | # - variable_name 131 | # - functionName 132 | # - ClassName 133 | # - CONSTANTS 134 | 135 | 136 | ################################### 137 | 138 | # NUMPY! 139 | 140 | # Importing numpy 141 | import numpy as np 142 | 143 | # 2 points in 3D space 144 | a = [[5, 1, 8], [3, 4, 6]] 145 | 146 | print(a) 147 | 148 | # Converting the list to a numpy array 149 | a = np.array(a) 150 | 151 | print(a) 152 | 153 | # Atrubutes of a numpy array 154 | print('Shape:', a.shape) 155 | print('Dimensions:', a.ndim) 156 | print('Type:', a.dtype) 157 | print('Total number of elements:', a.size) 158 | 159 | # Creating an array of zeros 160 | b = np.zeros((3, 3)) 161 | 162 | print(b) 163 | print(b.dtype) 164 | 165 | # Creating an array of ones, forcing the type to int 166 | c = np.ones((3, 3), dtype=np.int32) 167 | 168 | print(c) 169 | 170 | # Identity matrix 171 | idm = np.eye(3) 172 | print(idm) 173 | 174 | # Converting the string list to a float list 175 | num_lst = ['1.20', '2.15', '3.78', '4.05'] 176 | 177 | num_lst = np.array(num_lst).astype(np.float64) 178 | 179 | print(num_lst) 180 | 181 | 182 | # Creating a range of numbers from 0 to 100 as an array, with step 0.5 183 | arr = np.arange(0, 100, 0.5) 184 | 185 | print (arr) 186 | 187 | # Creating a range of numbers - another approach 188 | arr2 = np.linspace(0, 2, 9) 189 | 190 | print(arr2) 191 | 192 | # Reshaping an array 193 | arr2 = arr2.reshape((3, 3)) 194 | 195 | print(arr2) 196 | 197 | ################################### 198 | 199 | ### Operations between arrays/matrices 200 | 201 | # Adding a scalar to an array 202 | a = np.zeros((3, 3)) 203 | a = a + 5 204 | print(a) 205 | 206 | # Multiplying by a scalar 207 | a = a*3 208 | print(a) 209 | 210 | # Raising to a power 211 | a = a**2 212 | print(a) 213 | 214 | 215 | a = np.zeros((3, 3)) + 2 216 | b = np.zeros_like(a) + 3 217 | 218 | # Multiplying matrices, element-wise 219 | print(a*b) 220 | 221 | # Dot product (matrix product) 222 | print(np.dot(a, b)) 223 | 224 | # Some unary operations 225 | a = np.arange(1, 101) 226 | print('Sum:', np.sum(a)) 227 | print('Min:', np.min(a)) 228 | print('Max:', np.max(a)) 229 | print('Mean:', np.mean(a)) 230 | print('Median:', np.median(a)) 231 | print('Stddev:', np.std(a)) 232 | 233 | ################################### 234 | 235 | arr = np.linspace(1, 5, 20) 236 | 237 | ### Mathematical functions 238 | 239 | # Square root 240 | print(np.sqrt(arr)) 241 | 242 | # Sine 243 | print(np.sin(arr)) 244 | 245 | # Cosine 246 | print(np.cos(arr)) 247 | 248 | # Exponential 249 | print(np.exp(arr)) 250 | 251 | 252 | ################################### 253 | 254 | ### INTRO TO MATPLOTLIB 255 | 256 | # Basic anatomy of a plot: https://matplotlib.org/_images/anatomy1.png 257 | 258 | 259 | import matplotlib.pyplot as plt 260 | 261 | # Sine function in the range x=[0, 10] 262 | x = np.linspace(0, 10, 100) 263 | y = np.sin(x) 264 | 265 | ### Basic plotting 266 | 267 | plt.plot(x, y) 268 | 269 | # plt.show() 270 | 271 | # Adding a 1 sigma above mean line 272 | plt.plot(x, np.zeros_like(x) + np.mean(y) + np.std(y), linestyle='--', color='red') 273 | 274 | # plt.show() 275 | 276 | # Adding a title and labels 277 | plt.title('My first plot') 278 | plt.xlabel('X') 279 | plt.ylabel('Y') 280 | 281 | # plt.show() 282 | 283 | # Adding a grid 284 | plt.grid() 285 | 286 | plt.show() 287 | 288 | # Clearing a plot 289 | plt.clf() 290 | plt.close() -------------------------------------------------------------------------------- /Lecture 6/L6_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | # Let's create some noisy data that should follow a line 7 | 8 | # Parameters of a line 9 | m = 2.6 10 | k = 10.8 11 | 12 | 13 | # Let's define a function describing a line 14 | def line(x, m, k): 15 | 16 | return m*x + k 17 | 18 | 19 | # Generate the line data 20 | x = np.linspace(0, 10, 100) 21 | line_data = line(x, m, k) 22 | 23 | # Add noise to the line 24 | line_data += np.random.normal(0, 1, line_data.shape) 25 | 26 | plt.hold(True) 27 | 28 | # Plot the line data 29 | plt.scatter(x, line_data) 30 | 31 | # plt.show() 32 | 33 | # Import needed scipy libraries 34 | import scipy.optimize 35 | 36 | # Fit the line 37 | popt, pcov = scipy.optimize.curve_fit(line, x, line_data) 38 | 39 | print('Fit params:', popt) 40 | 41 | plt.plot(x, line(x, *popt)) 42 | 43 | 44 | # plt.show() 45 | 46 | plt.clf() 47 | 48 | # See the documentation how to get the stddev of each parameter 49 | print('Stddev:', np.sqrt(np.diag(pcov))) 50 | 51 | # CONGRATS! YOUR FIRST SUCCESSFUL LINEAR REGRESSION IN PYTHON! 52 | 53 | # Why should we be very careful when we are doing any kind of regression: 54 | # Anscobe's Quartet: https://en.wikipedia.org/wiki/Anscombe%27s_quartet 55 | 56 | # What are the alternatives? 57 | # http://scikit-learn.org/stable/auto_examples/linear_model/plot_theilsen.html 58 | 59 | 60 | # Let's add some outliers to our data 61 | line_data[10:12] -= 30 62 | line_data[98:] += 50 63 | x[-1] += 10 64 | 65 | plt.scatter(x, line_data) 66 | 67 | # plt.show() 68 | 69 | # If we do the LS fit again... 70 | popt, pconv = scipy.optimize.curve_fit(line, x, line_data) 71 | 72 | # Plot fitted line 73 | plt.plot(x, line(x, *popt), label='LS') 74 | 75 | # Plot original line 76 | plt.plot(x, line(x, m, k), color='k', label='Original') 77 | 78 | # plt.show() 79 | 80 | # CHECK THAT scikit-learn is installed! 81 | from sklearn.linear_model import RANSACRegressor, TheilSenRegressor 82 | 83 | # Reshaping the data (required by the scikit functions) 84 | x = x.reshape(-1, 1) 85 | line_data = line_data.reshape(-1, 1) 86 | 87 | 88 | # RANSAC details: http://scipy-cookbook.readthedocs.io/items/RANSAC.html 89 | # RANSAC works on non-linear problems as well, often using in Computer Vision. 90 | 91 | # Init the RANSAC regressor 92 | ransac = RANSACRegressor() 93 | 94 | # Fit with RANSAC 95 | ransac.fit(x, line_data) 96 | 97 | # Get the fitted data result 98 | line_ransac = ransac.predict(x) 99 | 100 | # Show the RANSAC fit 101 | plt.plot(x, line_ransac, color='yellow', label='RANSAC') 102 | 103 | # plt.show() 104 | 105 | 106 | # Theil-Sen estimator: 107 | # General info: https://en.wikipedia.org/wiki/Theil%E2%80%93Sen_estimator 108 | # Good ONLY for LINEAR REGRESSION 109 | # Sci-kit learn implementation: http://scikit-learn.org/stable/auto_examples/linear_model/plot_theilsen.html 110 | 111 | # Init the Theil-Sen estimator instance 112 | theil = TheilSenRegressor() 113 | 114 | # Fit with the Theil-Sen estimator 115 | theil.fit(x, line_data) 116 | 117 | # Get the fitted data result 118 | line_theil = theil.predict(x) 119 | 120 | # Plot Theil-Sen results 121 | plt.plot(x, line_theil, color='red', label='Theil-Sen') 122 | 123 | plt.legend(loc='lower right') 124 | 125 | plt.show() 126 | 127 | plt.clf() 128 | 129 | ################################### 130 | 131 | # Minimization - e.g. how to find a minimum of a function? 132 | 133 | def f1(x): 134 | """ A tricky function to minimize. """ 135 | 136 | return 0.1*x**2 + 2*np.sin(2*x) 137 | 138 | 139 | x = np.linspace(-10, 10, 100) 140 | 141 | # Plot the tricky function 142 | plt.plot(x, f1(x)) 143 | 144 | # Try changing the inital estimage (first try 0, then try 5) 145 | x0 = 5 146 | 147 | # Find the minimum using BFGS algorithm 148 | res = scipy.optimize.minimize(f1, x0) 149 | 150 | # Find global minimum using basin hopping 151 | res = scipy.optimize.basinhopping(f1, x0, niter=2000) 152 | 153 | print (res.x) 154 | 155 | # Plot mimumum point 156 | plt.scatter(res.x, f1(res.x)) 157 | 158 | plt.show() 159 | plt.clf() 160 | 161 | 162 | 163 | ################################### 164 | 165 | # Fitting nonlinear models 166 | 167 | # Task 1 168 | 169 | 170 | 171 | 172 | 173 | ################################### 174 | ### NOT IN LECTURE 175 | 176 | ### EXTRA: ROBUST FIT attempt 177 | 178 | # Difficult function to fit 179 | def func(x, a, b, c, d, e): 180 | 181 | return a*np.sin(b*x) + c*x**2 + d*x + e 182 | 183 | 184 | x = np.linspace(0, 10, 1000) 185 | 186 | # Generte function data 187 | y_data = func(x, 1.5, 2, 0.1, 0.1, 3) 188 | 189 | # Plot the model data 190 | plt.plot(x, y_data, color='red', label='Underlying model') 191 | 192 | # Add noise 193 | y_data_noise = y_data + np.random.normal(0, 0.5, y_data.shape) 194 | 195 | # Plot noisy data 196 | plt.plot(x, y_data_noise, alpha=0.5, label='Noisy data') 197 | 198 | 199 | # Fit the function to the noisy data, regular LS 200 | popt, pcov = scipy.optimize.curve_fit(func, x, y_data_noise) 201 | 202 | # Plot LS fit results 203 | plt.plot(x, func(x, *popt), color='green', label='LS fit') 204 | 205 | # Read more about robust regression: 206 | # http://scipy-cookbook.readthedocs.io/items/robust_regression.html 207 | 208 | # Define a function for computing residuals 209 | def residuals(params, x, y): 210 | """ Returns the residuals between the predicted and input values of the model 211 | 212 | Arguments: 213 | params: [ndarray] function parameters 214 | x: [ndarray] independant variable 215 | y: [ndarray] prediction 216 | 217 | Return: 218 | residuals: [ndarray] 219 | 220 | """ 221 | 222 | return func(x, *params) - y 223 | 224 | 225 | # Set initial guess of parameters to 1 (array of size 5, same as the number of parameters of our function) 226 | x0 = np.ones(5) 227 | 228 | # Try to do a robust fit (doesn't always work, but it is better than ordinary LS) 229 | fit_robust_ls = scipy.optimize.least_squares(residuals, x0, loss='cauchy', f_scale=0.1, args=(x, y_data_noise)) 230 | 231 | 232 | 233 | def residuals_minimize(params, x, y): 234 | """ Wrapper function for calculating fit residuals for minimization. """ 235 | 236 | # Squared value of each residual 237 | z = residuals(params, x, y)**2 238 | 239 | # Smooth approximation of l1 (absolute value) loss 240 | return np.sum(2*((1 + z)**0.5 - 1)) 241 | 242 | 243 | # Treat the fit as a minimization problem, but use basinhopping for minimizing residuals 244 | fit_robust_mini = scipy.optimize.basinhopping(residuals_minimize, x0, minimizer_kwargs={'args':(x, y_data_noise)}) 245 | 246 | 247 | # Plot the robust fit results 248 | plt.plot(x, func(x, *fit_robust_ls.x), color='yellow', label='Robust fit - least squares') 249 | plt.plot(x, func(x, *fit_robust_mini.x), color='black', label='Robust fit - basinhopping') 250 | 251 | plt.legend(loc='lower right') 252 | plt.show() 253 | 254 | # For better results, Markov-Chain Monte Carlo fitting can be used: 255 | # https://sciencehouse.wordpress.com/2010/06/23/mcmc-and-fitting-models-to-data/ 256 | 257 | # MCMC Python implementation: 258 | # https://github.com/dvida/mcmc-fit-py/blob/master/MCMC%20fit.py -------------------------------------------------------------------------------- /Lecture 5/L5_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | ### Plotting, level 2 8 | x = np.linspace(0, 10, 100) 9 | 10 | plt.plot(x, np.sin(x), color='red', linestyle='--', label='Sine') 11 | plt.plot(x, np.sqrt(x), color='g', linewidth=10, label='Sqrt') 12 | plt.plot(x, np.tan(x), color='#FFD700', linestyle='', marker='o', label='Tan') 13 | 14 | # Adding a legend 15 | plt.legend(loc='upper left') 16 | 17 | # plt.show() 18 | 19 | # Forcing plot limits 20 | plt.xlim((0, 5)) 21 | plt.ylim((0, 2)) 22 | 23 | plt.show() 24 | 25 | plt.clf() 26 | plt.close() 27 | 28 | 29 | ### Plotting, level 3 30 | 31 | x = np.linspace(1, 100, 1000) 32 | 33 | plt.plot(x, np.log10(x)+np.sin(x)) 34 | 35 | # Setting a logarithmic scale 36 | plt.xscale('log') 37 | 38 | # Turn on grid (both major and minor grid lines) 39 | plt.grid(which='both') 40 | 41 | plt.show() 42 | 43 | plt.clf() 44 | plt.close() 45 | 46 | 47 | ### Plotting, level 4 48 | 49 | # Making several subplots 50 | fig, (ax1, ax2) = plt.subplots(nrows = 2) #, sharex=True) 51 | 52 | x1 = np.linspace(0, 10, 100) 53 | ax1.plot(x1, np.sin(x1), color='g') 54 | 55 | x2 = np.linspace(5, 15, 100) 56 | ax2.plot(x2, np.cos(x2), color='r') 57 | 58 | ax1.grid() 59 | ax2.grid() 60 | 61 | # fig.subplots_adjust(hspace=0) 62 | 63 | plt.show() 64 | 65 | # Uncomment sharex and fig.subplots_adjust for sharing the X axis 66 | 67 | plt.clf() 68 | plt.close() 69 | 70 | 71 | ### Plotting, level 5 72 | 73 | x = np.linspace(-np.pi, np.pi, 100) 74 | plt.plot(x, np.sin(x), color='r', label='Sine') 75 | plt.plot(x, np.cos(x), color='b', label='Cosine') 76 | 77 | # Setting ticks in LaTeX style 78 | plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], ['$-\pi$', '$-\pi/2$', '$0$', '$\pi/2$', '$\pi$']) 79 | plt.yticks([-1, 0, 1], ['$-1$', '$0$', '$1$']) 80 | 81 | plt.xlim((-np.pi, np.pi)) 82 | 83 | plt.legend(loc='upper left', frameon=False) 84 | 85 | # plt.show() 86 | 87 | # Moving spines (JUST C/P, NO DETAILED EXPLANATOIN!) 88 | ax = plt.gca() 89 | ax.spines['right'].set_color('none') 90 | ax.spines['top'].set_color('none') 91 | ax.xaxis.set_ticks_position('bottom') 92 | ax.spines['bottom'].set_position(('data',0)) 93 | ax.yaxis.set_ticks_position('left') 94 | ax.spines['left'].set_position(('data',0)) 95 | 96 | plt.show() 97 | 98 | plt.clf() 99 | plt.close() 100 | 101 | 102 | ################################### 103 | 104 | ### Numpy continued 105 | 106 | a = np.arange(100, 200) 107 | 108 | # Indexing works the same as with lists 109 | print(a[0]) 110 | print(a[-1]) 111 | 112 | print(a[5:20]) 113 | 114 | # Reshape to 2 columns 115 | a = a.reshape((-1, 2)) 116 | 117 | print(a) 118 | 119 | # Accessing 2D elements - NOTICE: slightly different than lists! 120 | print(a[5,0]) 121 | 122 | # Array slicing 123 | 124 | first_row = a[0,:] 125 | print(first_row) 126 | 127 | first_column = a[:,0] 128 | print(first_column) 129 | 130 | second_column = a[:,1] 131 | print(second_column) 132 | 133 | print(a.shape) 134 | 135 | # Splitting arrays - by column 136 | first_column, second_column = np.hsplit(a, 2) 137 | print(first_column) 138 | print(second_column) 139 | 140 | # For splitting by row, use vsplit 141 | 142 | ################################### 143 | 144 | # USING NUMPY DOCUMENTATION: 145 | # Google: numpy sample uniform 146 | 147 | 148 | # Sampling distributions 149 | 150 | # Uniform distribution 151 | uniform_sample = np.random.uniform(0, 1, 10) 152 | 153 | print(uniform_sample) 154 | 155 | # Normal distribution 1D 156 | normal_sample = np.random.normal(0, 1.0, 200) 157 | 158 | print(normal_sample) 159 | 160 | # Taking samples from an existing array 161 | samples = np.random.choice(normal_sample, 10) 162 | 163 | print(samples) 164 | 165 | # Conditional filtering - taking only positive numbers in the normal sample 166 | print(normal_sample[normal_sample > 0]) 167 | 168 | # Conditionally modifying an array, overwriting zero values 169 | uniform_sample[uniform_sample > 0.5] = 0 170 | 171 | print(uniform_sample) 172 | 173 | ################################### 174 | 175 | # Numpy - linear algebra: 176 | # https://docs.scipy.org/doc/numpy/reference/routines.linalg.html 177 | 178 | ################################### 179 | # FOR THOSE WHO WANT TO KNOW MORE 180 | 181 | # Plotting a histogram of our normal sample 182 | plt.hist(normal_sample, bins=10)#, cumulative=True) 183 | 184 | plt.show() 185 | 186 | plt.clf() 187 | plt.close() 188 | 189 | ################################### 190 | 191 | # IMPORTANT: Shallow vs deep copy 192 | a = np.arange(10) 193 | 194 | print(a) 195 | 196 | # Assigning the array to another variable (not not copying it!) 197 | b = a 198 | 199 | # Modifying 'b' will also modify 'a' 200 | b[0] = 100 201 | 202 | print(a) 203 | 204 | # The proper way of copying arrays 205 | b = np.copy(a) 206 | 207 | a[0] = 9 208 | 209 | print('a:', a) 210 | print('b:', b) 211 | 212 | ################################### 213 | 214 | # Sorting arrays 215 | a = np.random.uniform(0, 10, 10) 216 | 217 | print(np.sort(a)) 218 | 219 | # Sorting by the second column 220 | a = np.random.uniform(0, 10, (20, 2)) 221 | a = a[a[:,1].argsort()] 222 | 223 | print(a) 224 | 225 | ################################### 226 | 227 | # Showing images 228 | 229 | a = np.linspace(0, 255, 200*200).reshape(200, 200)#.T 230 | 231 | # Showing an image - IMPORTANT: DIFFERENT COLORMAPS 232 | # Recommended colormaps: inferno, magma, viridis, gray 233 | # Not recommended: jet 234 | plt.imshow(a, cmap='inferno') 235 | 236 | plt.show() 237 | 238 | # Show the TRANSPOSING! 239 | 240 | plt.clf() 241 | plt.close() 242 | 243 | ################################### 244 | 245 | # Plotting 3D data using scatter and color 246 | x = np.random.rand(100) 247 | y = np.random.rand(100) 248 | z = np.arange(100) 249 | 250 | plt.scatter(x, y, c=z, edgecolors='none') 251 | plt.colorbar(label='my data') 252 | 253 | plt.show() 254 | 255 | plt.clf() 256 | plt.close() 257 | 258 | 259 | ################################### 260 | # FOR THOSE WHO WANT TO KNOW MORE 261 | 262 | # Contour plots 263 | 264 | import matplotlib.mlab as mlab 265 | 266 | # Define the size of the plot (Xmin, Xmax, Ymin, Ymax) 267 | extent = (-3, 3, -2, 2) 268 | 269 | # 'Resolution' of the plot 270 | delta = 0.025 271 | 272 | x = np.arange(extent[0], extent[1], delta) 273 | y = np.arange(extent[2], extent[3], delta) 274 | 275 | # Generate the background 'grid' for the data 276 | X, Y = np.meshgrid(x, y) 277 | 278 | # Generate 2 bivariate Gaussian distributions 279 | Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) 280 | Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) 281 | 282 | # Difference of Gaussians (so we can get some nice data for plotting) 283 | Z = 10.0 * (Z2 - Z1) 284 | 285 | # Define contour levels (from -1 to 1.5 with 0.25 step) 286 | levels = np.arange(-1.0, 1.5, 0.25) 287 | 288 | # Plot the image (set the extent to the data size, otherwise the image size would be dimension/delta) 289 | plt.imshow(Z, extent=extent, cmap='inferno') 290 | 291 | # Plot the contours with the given levels (omit the 'levels' for auto levels) 292 | # NOTE: origin='upper' must be set, as images usually start in the upper left corner - if this was not set, 293 | # the contours would be upside down 294 | CS = plt.contour(Z, levels=levels, origin='upper', extent=extent) 295 | 296 | # Label the contours (inline=1 creates a break in countour lines for inserting the text) 297 | plt.clabel(CS, inline=1, fontsize=10) 298 | 299 | plt.show() -------------------------------------------------------------------------------- /Lecture 9/L9_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | ### Multiprocessing and parallelization 4 | # See file: PyDomanParallelizer.py 5 | 6 | ### 7 | 8 | 9 | ######################################### 10 | 11 | ### Exceptions ### 12 | 13 | 14 | # Let's cause some errors just for fun, and see what happens: 15 | 16 | # # Zero division: 17 | # print(5/0) 18 | 19 | # # Accessing non-existent element in a list 20 | # a = [1, 2, 3] 21 | # print(a[10]) 22 | 23 | # # Type error 24 | # print('a' + 2) 25 | 26 | # You can see what types of errors are caused, and try to predict which error you want to catch. 27 | 28 | import numpy as np 29 | 30 | def invSqrt(x): 31 | """ Returnes the inverse square root of a number, and returns infinity is the given number is 0. """ 32 | 33 | try: 34 | return 1/np.sqrt(x) 35 | 36 | except ZeroDivisionError: 37 | return np.inf 38 | 39 | 40 | 41 | print(invSqrt(5)) 42 | print(invSqrt(0)) 43 | 44 | # # What if we give it some nonsense? 45 | # print(invSqrt('a')) 46 | 47 | # We can handle all other errors by a general 'except' block - be careful when using this, as you can let 48 | # some unpredicted errors slip through! 49 | 50 | try: 51 | # Printing nonexistent variable 52 | print(result) 53 | except: 54 | print('There was an error!') 55 | 56 | 57 | # Check the documentation for more information: 58 | # https://docs.python.org/3/tutorial/errors.html 59 | 60 | 61 | ######################################### 62 | 63 | ### Decorators ### 64 | # An extremely powerful Python feature 65 | 66 | 67 | import time 68 | 69 | # Decorator for timing function execution time 70 | 71 | def timeItDeco(func): 72 | """ Decorator which times the given function. """ 73 | 74 | def timing(*args, **kwargs): 75 | """ This function will replace the original function. """ 76 | 77 | # Start the clock 78 | t1 = time.clock() 79 | 80 | # Run the original function and collect results 81 | result = func(*args, **kwargs) 82 | 83 | # Print out the execution time 84 | print('Execution time', time.clock() - t1) 85 | 86 | return result 87 | 88 | # Return the funtion that was modified 89 | return timing 90 | 91 | 92 | 93 | # @timeItDeco 94 | def calcPi(n): 95 | """ Calculating Pi using Monte Carlo Integration. 96 | 97 | Quickly estimates the first 2 decimals, but it is terribly inefficient for estimating other decimals. 98 | 99 | Source: http://www.stealthcopter.com/blog/2009/09/python-calculating-pi-using-random-numbers/ 100 | """ 101 | 102 | inside = 0.0 103 | 104 | for i in range(n): 105 | x = np.random.random() 106 | y = np.random.random() 107 | 108 | # Calculate the length of hypotenuse given the sides x and y 109 | if np.hypot(x, y) <= 1: 110 | inside += 1 111 | 112 | return 4.0*inside/n 113 | 114 | # Run the calcPi function without timing 115 | print(calcPi(100000)) 116 | 117 | 118 | # Wrap it inside a decorator 119 | calcPi = timeItDeco(calcPi) 120 | 121 | # Run the decorated function 122 | print(calcPi(100000)) 123 | 124 | # You can also do this by putting @timeItDeco above the function declaration! 125 | 126 | ### 127 | 128 | 129 | def memoDeco(func): 130 | """ Decorator which stores results of previous function calls, and for all future function calls looks up 131 | the result in memory before calculating it. 132 | 133 | """ 134 | 135 | cache = {} 136 | 137 | def mem(*args, **kwargs): 138 | 139 | # Check if we alreasy have a solution stored 140 | if args in cache: 141 | return cache[args] 142 | 143 | # If not, calculate it and add to cache 144 | else: 145 | 146 | # Calculate the result 147 | result = func(*args, **kwargs) 148 | 149 | # Store the result to cache 150 | cache[args] = result 151 | 152 | return result 153 | 154 | return mem 155 | 156 | 157 | # @memoDeco 158 | def fib(n): 159 | """ Calculate the Nth Fibonacci number. This is an EXTREMELY inefficient function. 160 | """ 161 | 162 | if n in (0, 1): 163 | return n 164 | 165 | return fib(n-1) + fib(n-2) 166 | 167 | 168 | # This takes a while without memoization, but it is lightning fast with memoization 169 | 170 | t1 = time.clock() 171 | 172 | print(fib(35)) 173 | 174 | print('Fibonacci runtime:', time.clock() - t1) 175 | 176 | 177 | ######################################### 178 | 179 | ### Regular expressions ### 180 | 181 | # "Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' 182 | # Now they have two problems." - Jamie Zawinski 183 | 184 | # Regular expression is a sequence of characters that define a search pattern. 185 | 186 | 187 | ## Possible seach pattern elements: 188 | 189 | # . (dot) - matches any character except a newline 190 | 191 | # * (star) - matches 0 or more repetitions of the preceding RE, as many repetitions as are possible. 192 | # e.g. if our regular expression was: a*, it will match: '' (EMPTY), 'a', 'aa', 'aaa', etc. 193 | 194 | # + (plus) - matches 1 or more repetitions of the preceding RE, as many repetitions as are possible. 195 | # e.g. if our regular expression was: a+, it will match: 'a', 'aa', 'aaa', etc. 196 | 197 | # ? (question mark) - matches 0 or 1 repetion of the preceding RE: 198 | # e.g. if our regular expression was: a?, it will match: '' (EMPTY) and 'a'. 199 | 200 | # The '*', '+', and '?' qualifiers are all greedy - they match as much text as possible. 201 | 202 | # {m} - matches exactly m repetitions of the preceding RE. 203 | # e.g. if our regular expression was: a{5}, it will match 'aaaaa' 204 | 205 | # {m, n} - matches m to n repetitions of the preceding RE. 206 | # e.g. if our regular expression was: a{2, 5}, it will match 'aa', 'aaa', 'aaaa', 'aaaaa' 207 | 208 | # [] - used to indicate a set of characters. In a set: 209 | # - Characters can be listed individually, e.g. [amk] will match 'a', 'm', or 'k'. 210 | # - Ranges of characters can be indicated by giving two characters and separating them by a '-', for 211 | # example [a-z] will match any lowercase ASCII letter, [0-5][0-9] will match all the two-digits 212 | # numbers from 00 to 59. 213 | 214 | # | - A|B, where A and B can be arbitrary REs, creates a regular expression that will match either A or B. 215 | # It is basically the 'or' logical operator. 216 | 217 | # () - defines a group which will be consided a RE. 218 | # E.g. if we want to match all occurences of the 'cat' in a string, we would write (cat)* 219 | 220 | import re 221 | 222 | 223 | # Let's match all repetitions of the letter 'a' 224 | s = 'a aa aaa' 225 | 226 | # Find all occurences and their indices of start and end 227 | match = re.finditer('a+', s) 228 | 229 | # Go through all matches 230 | for group in match: 231 | 232 | start = group.start() 233 | end = group.end() 234 | 235 | print(start, end, s[start:end]) 236 | 237 | 238 | 239 | # Let's write a regular expression which will match phone numbers in this format: 240 | tel_num = '031-212-555' 241 | 242 | match = re.findall('[0-9]{3}-[0-9]{3}-[0-9]{3}', tel_num) 243 | 244 | if match: 245 | print(tel_num, 'is a proper telephone number!') 246 | 247 | else: 248 | print('Wrong format!') 249 | 250 | # Now try to change the telephone number to be of another format! 251 | 252 | 253 | # In a list of courses, let's find all astronomy courses about Mars 254 | courses = [ 255 | 'ASTRO 101 - Intro', 256 | 'ASTRO 206 - Earth', 257 | 'ASTRO 287 - Intro to Mars', 258 | 'ASTRO 457 - Marsian atmosphere' 259 | ] 260 | 261 | for course in courses: 262 | match = re.findall('ASTRO [0-9]{3} - .*Mars.*', course) 263 | 264 | if match: 265 | print(match) -------------------------------------------------------------------------------- /Lecture 8/L8_lecture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import numpy as np 4 | 5 | 6 | 7 | ######################################### 8 | 9 | ### Generators ### 10 | # Functions which return next value in a sequence upon each call 11 | 12 | def squares(): 13 | """ Generator which returns the square value of every integer, starting with 1. """ 14 | 15 | i = 1 16 | 17 | while True: 18 | 19 | # Return a squared number 20 | yield i**2 21 | 22 | i += 1 23 | 24 | # Init the generator 25 | sq = squares() 26 | 27 | print(next(sq)) 28 | print(next(sq)) 29 | print(next(sq)) 30 | print(next(sq)) 31 | 32 | 33 | 34 | # Generator which exchanges between True and False upon every call 35 | def truthGenerator(): 36 | 37 | while True: 38 | yield True 39 | yield False 40 | 41 | # Init the generator 42 | truth = truthGenerator() 43 | 44 | print(next(truth)) 45 | print(next(truth)) 46 | print(next(truth)) 47 | print(next(truth)) 48 | 49 | 50 | 51 | # Let's replace the lottery hostess with a robot... 52 | 53 | import random 54 | 55 | def lottery(): 56 | 57 | # Returns 6 numbers between 1 and 40 58 | for i in range(6): 59 | yield random.randint(1, 40) 60 | 61 | # Returns a 7th number between 1 and 15 62 | yield random.randint(1,15) 63 | 64 | 65 | for rand_num in lottery(): 66 | print("And the next number is...", rand_num) 67 | 68 | 69 | # Now we can run our own illegal gambling den! 70 | 71 | 72 | 73 | ######################################### 74 | 75 | 76 | ### Introduction to object oriented programming ### 77 | 78 | 79 | # Make a class which will describe a battleship 80 | class Battleship: 81 | 82 | def __init__(self, name, operator, displacement, sunk=False): 83 | 84 | # Name of the battleship 85 | self.name = name 86 | 87 | # Operator/country 88 | self.operator = operator 89 | 90 | # Displacement in metric tonnes 91 | self.displacement = displacement 92 | 93 | # List of armament (number and caliber) 94 | self.armament = [] 95 | 96 | self.sunk = sunk 97 | 98 | # Game parameters 99 | self.battle_power = 0 100 | self.hp = displacement 101 | 102 | 103 | def addArmament(self, num, caliber): 104 | 105 | self.armament.append([num, caliber]) 106 | 107 | # Calculate the arbitrary battle power parameter 108 | self.battle_power += num*caliber**2 109 | 110 | 111 | 112 | # Init a battleship object for Austria-Hungary 113 | vb = Battleship('SMS Viribus Unitis', 'Austria-Hungary', 20000) 114 | 115 | # Add armament 116 | vb.addArmament(12, 12) # 4x12 inch guns 117 | vb.addArmament(12, 15) 118 | vb.addArmament(12, 7) 119 | 120 | # Init a battleship for Italy 121 | da = Battleship('Dante Alighieri', 'Italy', 19500) 122 | 123 | # Add armament 124 | da.addArmament(12, 12) 125 | da.addArmament(20, 4.7) 126 | 127 | 128 | 129 | def engage(ship1, ship2): 130 | """ Confront two battleships. """ 131 | 132 | # Exchange volleys until one of the ships is sunk 133 | while True: 134 | 135 | # Reduce health points of the 1st ship 136 | ship1.hp -= ship2.battle_power 137 | 138 | # Reduce health points of the 2nd ship 139 | ship2.hp -= ship1.battle_power 140 | 141 | # Check if the 1st ship is sunk 142 | if ship1.hp < 0: 143 | 144 | ship1.sunk = True 145 | print(ship1.name, 'was sunk!') 146 | 147 | break 148 | 149 | # Check if the 2nd ship is sunk 150 | elif ship2.hp < 0: 151 | 152 | ship2.sunk = True 153 | print(ship2.name, 'was sunk!') 154 | 155 | break 156 | 157 | 158 | # Confront Viribus Unitis and Dante Alighieri 159 | engage(vb, da) 160 | 161 | 162 | 163 | 164 | # Operator overloading and printable representation of an object 165 | 166 | class Sphere: 167 | 168 | def __init__(self, volume): 169 | 170 | self.volume = volume 171 | self.radius = (self.volume/(4/3*np.pi))**(1/3) 172 | 173 | 174 | def __add__(self, other): 175 | 176 | merged = Sphere(self.volume + other.volume) 177 | 178 | return merged 179 | 180 | 181 | def __repr__(self): 182 | return 'Sphere of volume ' + str(self.volume) + ' m^3 and radius ' + str(self.radius) + ' m' 183 | 184 | 185 | # More on operator overloading: https://docs.python.org/3/library/operator.html 186 | 187 | 188 | s1 = Sphere(10) # 10 m3 volume 189 | s2 = Sphere(2) # 2 m3 volume 190 | 191 | print(s1) 192 | print(s2) 193 | 194 | # Add to spheres together 195 | s3 = s1 + s2 196 | 197 | # Check the new radius 198 | print(s3) 199 | 200 | 201 | # Class inheritance 202 | 203 | class AstroObj: 204 | def __init__(self, name, ra, dec): 205 | 206 | self.name = name 207 | self.ra = ra 208 | self.dec = dec 209 | 210 | def angDist(self, other): 211 | """ Calculate the angular distance between two astronomical objects. """ 212 | 213 | ra1 = np.radians(self.ra) 214 | dec1 = np.radians(self.dec) 215 | 216 | ra2 = np.radians(other.ra) 217 | dec2 = np.radians(other.dec) 218 | 219 | ang = np.sin(dec1)*np.sin(dec2) + np.cos(dec1)*np.cos(dec2)*np.cos(ra1 - ra2) 220 | 221 | return np.degrees(np.arccos(ang)) 222 | 223 | 224 | class Star(AstroObj): 225 | 226 | def __init__(self, name, ra, dec, spec_type): 227 | 228 | # Extend AstroObj 229 | AstroObj.__init__(self, name, ra, dec) 230 | 231 | self.spec_type = spec_type 232 | 233 | 234 | 235 | class Galaxy(AstroObj): 236 | 237 | def __init__(self, name, ra, dec, z): 238 | 239 | # Extend AstroObj 240 | AstroObj.__init__(self, name, ra, dec) 241 | 242 | self.z = z 243 | 244 | 245 | 246 | s1 = Star('Sirius', 101.2875, -16.7161, 'DA2') 247 | g1 = Galaxy('NGC660', 25.7583, +13.645, 0.003) 248 | 249 | print(s1.angDist(g1)) 250 | 251 | 252 | ######################################### 253 | 254 | 255 | # Everything in Python is an object! 256 | # E.g. we can do something like this: 257 | 258 | a = [1, 2, 3] 259 | 260 | # This will give us the length of list 'a', because it is stored as its attribute 261 | print(a.__len__) 262 | 263 | 264 | ######################################### 265 | 266 | 267 | ### List comprehension ### 268 | # Transforming one list to another 269 | 270 | 271 | # List of even numbers from 1 to 100 272 | evens = [x for x in range(1, 101) if x%2 == 0] 273 | 274 | # For easier understanding of the line above, let's convert it to words: 275 | # "Take a number in a range from 1 to 100, only if it is divisible by 2 276 | 277 | print(evens) 278 | 279 | 280 | ### C/P to show the equivalent code 281 | evens = [] 282 | for x in range(1, 101): 283 | if x%2 == 0: 284 | evens.append(x) 285 | 286 | print(evens) 287 | 288 | ### 289 | 290 | 291 | # Let's unravel a 2D list 292 | a = [[1, 2], [3, 4]] 293 | 294 | a = [x for row in a for x in row] 295 | 296 | print(a) 297 | 298 | 299 | 300 | # The classic "Mathematicians order pizzas" joke: 301 | # An infinite number of mathematicians enter a pizzeria. The first mathematician orders 1 pizza. The second 302 | # one orders 1/2 of a pizza, the third one orders 1/4, the fourth one orders 1/8, etc. 303 | # The server quickly looses this temper and just brings them 2 pizzas. Was he right? 304 | 305 | pizzas = [1.0/(2**x) for x in range(50)] 306 | 307 | # We see that the number quickly converges to 0, so we can use only 100 numbers 308 | print(pizzas) 309 | 310 | # The sum of all pizzas 311 | print('Infinite pizzas:', sum(pizzas)) 312 | 313 | 314 | 315 | ######################################### 316 | 317 | # Question: 318 | # Describe what will the 'form' list contain 319 | 320 | lst = [4.1756, 2.3412, 8.5754, 7.124531] 321 | 322 | form = ["x[{:d}] = {:5.2f}".format(i, x) for i, x in enumerate(lst)] 323 | 324 | print(form) 325 | 326 | 327 | ######################################### 328 | 329 | 330 | ### Dictionaries ### 331 | # A collection of (key: value) pairs 332 | 333 | num2word = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four'} 334 | 335 | # Print 'zero' 336 | print(num2word[0]) 337 | 338 | # Go through all keys in the dictionary and return values 339 | for key in num2word: 340 | print(num2word[key]) 341 | 342 | 343 | ######################################### 344 | 345 | 346 | ### Sets ### 347 | # Lists of unique elements 348 | 349 | a = [1, 1, 2, 2, 2, 3, 4, 5, 6, 6, 7] 350 | 351 | # Convert a to a set 352 | b = set(a) 353 | 354 | 355 | # WARNING! 356 | # We cannot index sets! 357 | # This return an ERROR: 358 | # print(b[0]) 359 | 360 | # If you want it back as a list, you could do: 361 | # b = list(set(a)) 362 | 363 | 364 | c = set([2, 3, 10]) 365 | 366 | # Get the difference of two sets 367 | print(b.difference(c)) 368 | 369 | # Get the intersection of two sets 370 | print(b.intersection(c)) 371 | 372 | # More: check if one set is a subset of another, check if they are disjoint (their intersection is null) -------------------------------------------------------------------------------- /Lecture 3/Task 1/L3_T1_generate_data.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | import shutil 4 | import os 5 | 6 | data_dir = 'data' 7 | 8 | # Remove old data dir if it exists 9 | if os.path.isdir(data_dir): 10 | shutil.rmtree(data_dir) 11 | 12 | # Make a new data dir 13 | os.mkdir(data_dir) 14 | 15 | file_list = ["HL_fri_nov_25_00:04:15_2016_033253.eps.png", "HL_fri_nov_25_00:15:51_2016_468285.eps.png", "HL_fri_nov_25_00:26:07_2016_424044.eps.png", "HL_fri_nov_25_00:36:19_2016_014794.eps.png", "HL_fri_nov_25_00:46:31_2016_301042.eps.png", "HL_fri_nov_25_00:58:33_2016_576374.eps.png", "HL_fri_nov_25_01:09:50_2016_171095.eps.png", "HL_fri_nov_25_01:21:27_2016_821116.eps.png", "HL_fri_nov_25_01:31:59_2016_210636.eps.png", "HL_fri_nov_25_01:43:01_2016_024221.eps.png", "HL_fri_nov_25_01:56:17_2016_163561.eps.png", "HL_fri_nov_25_02:06:57_2016_600605.eps.png", "HL_fri_nov_25_02:17:26_2016_856217.eps.png", "HL_fri_nov_25_02:27:54_2016_370336.eps.png", "HL_fri_nov_25_02:39:09_2016_575070.eps.png", "HL_fri_nov_25_02:49:19_2016_052853.eps.png", "HL_fri_nov_25_03:00:31_2016_774194.eps.png", "HL_fri_nov_25_03:11:05_2016_818719.eps.png", "HL_fri_nov_25_03:22:49_2016_341189.eps.png", "HL_fri_nov_25_03:33:46_2016_173341.eps.png", "HL_fri_nov_25_03:46:28_2016_387774.eps.png", "HL_fri_nov_25_03:56:57_2016_160876.eps.png", "HL_fri_nov_25_04:09:13_2016_574495.eps.png", "HL_fri_nov_25_04:19:23_2016_574824.eps.png", "HL_fri_nov_25_04:29:34_2016_357810.eps.png", "HL_fri_nov_25_04:39:49_2016_892939.eps.png", "HL_fri_nov_25_04:50:16_2016_922411.eps.png", "HL_fri_nov_25_05:00:28_2016_174272.eps.png", "HL_fri_nov_25_05:10:44_2016_010048.eps.png", "HL_fri_nov_25_05:20:55_2016_476824.eps.png", "HL_fri_nov_25_05:33:15_2016_576613.eps.png", "HL_fri_nov_25_05:44:05_2016_853479.eps.png", "HL_fri_nov_25_05:56:32_2016_457130.eps.png", "HL_fri_nov_25_06:06:56_2016_509904.eps.png", "HL_fri_nov_25_06:17:16_2016_982551.eps.png", "HL_fri_nov_25_06:28:03_2016_122523.eps.png", "HL_fri_nov_25_06:39:30_2016_231969.eps.png", "HL_fri_nov_25_06:49:51_2016_158985.eps.png", "HL_fri_nov_25_07:01:51_2016_202519.eps.png", "HL_fri_nov_25_07:12:59_2016_931034.eps.png", "HL_fri_nov_25_07:24:21_2016_185496.eps.png", "HL_fri_nov_25_07:36:55_2016_357616.eps.png", "HL_fri_nov_25_07:48:35_2016_984875.eps.png", "HL_fri_nov_25_08:00:06_2016_920938.eps.png", "HL_fri_nov_25_08:10:13_2016_202902.eps.png", "HL_fri_nov_25_08:20:49_2016_488350.eps.png", "HL_fri_nov_25_08:32:46_2016_597395.eps.png", "HL_fri_nov_25_08:45:32_2016_420212.eps.png", "HL_fri_nov_25_08:56:03_2016_762341.eps.png", "HL_fri_nov_25_09:06:39_2016_155739.eps.png", "HL_fri_nov_25_09:17:08_2016_716355.eps.png", "HL_fri_nov_25_09:27:22_2016_528766.eps.png", "HL_fri_nov_25_09:38:04_2016_171726.eps.png", "HL_fri_nov_25_09:48:20_2016_406291.eps.png", "HL_fri_nov_25_09:58:34_2016_609739.eps.png", "HL_fri_nov_25_10:09:15_2016_234176.eps.png", "HL_fri_nov_25_10:20:10_2016_417699.eps.png", "HL_fri_nov_25_10:31:12_2016_233430.eps.png", "HL_fri_nov_25_10:41:36_2016_214235.eps.png", "HL_fri_nov_25_10:51:45_2016_106601.eps.png", "HL_fri_nov_25_11:02:14_2016_132869.eps.png", "HL_fri_nov_25_11:13:44_2016_894160.eps.png", "HL_fri_nov_25_11:23:58_2016_343611.eps.png", "HL_fri_nov_25_11:34:10_2016_528914.eps.png", "HL_fri_nov_25_11:46:04_2016_411380.eps.png", "HL_fri_nov_25_11:57:16_2016_267828.eps.png", "HL_fri_nov_25_12:08:25_2016_844841.eps.png", "HL_fri_nov_25_12:19:28_2016_935048.eps.png", "HL_fri_nov_25_12:31:35_2016_444374.eps.png", "HL_fri_nov_25_12:42:23_2016_505704.eps.png", "HL_fri_nov_25_12:52:34_2016_640087.eps.png", "HL_fri_nov_25_13:04:09_2016_880599.eps.png", "HL_fri_nov_25_13:14:18_2016_783008.eps.png", "HL_fri_nov_25_13:24:41_2016_189429.eps.png", "HL_fri_nov_25_13:35:49_2016_462911.eps.png", "HL_fri_nov_25_13:47:56_2016_239200.eps.png", "HL_fri_nov_25_13:58:51_2016_527288.eps.png", "HL_fri_nov_25_14:09:24_2016_720131.eps.png", "HL_fri_nov_25_14:19:46_2016_044152.eps.png", "HL_fri_nov_25_14:30:19_2016_503202.eps.png", "HL_fri_nov_25_14:40:35_2016_007314.eps.png", "HL_fri_nov_25_14:50:45_2016_501117.eps.png", "HL_fri_nov_25_15:01:19_2016_962726.eps.png", "HL_fri_nov_25_15:11:57_2016_065410.eps.png", "HL_fri_nov_25_15:22:11_2016_335065.eps.png", "HL_fri_nov_25_15:32:58_2016_759901.eps.png", "HL_fri_nov_25_15:43:09_2016_221333.eps.png", "HL_fri_nov_25_15:55:12_2016_598676.eps.png", "HL_fri_nov_25_16:05:51_2016_987360.eps.png", "HL_fri_nov_25_16:17:02_2016_889378.eps.png", "HL_fri_nov_25_16:28:27_2016_796607.eps.png", "HL_fri_nov_25_16:38:53_2016_410227.eps.png", "HL_fri_nov_25_16:49:27_2016_099227.eps.png", "HL_fri_nov_25_17:01:43_2016_147595.eps.png", "HL_fri_nov_25_17:11:57_2016_187190.eps.png", "HL_fri_nov_25_17:22:21_2016_624519.eps.png", "HL_fri_nov_25_17:33:15_2016_832765.eps.png", "HL_fri_nov_25_17:43:40_2016_605550.eps.png", "HL_fri_nov_25_17:54:01_2016_441824.eps.png", "HL_fri_nov_25_18:04:30_2016_766140.eps.png", "HL_fri_nov_25_18:16:52_2016_694904.eps.png", "HL_fri_nov_25_18:27:20_2016_067088.eps.png", "HL_fri_nov_25_18:37:34_2016_944711.eps.png", "HL_fri_nov_25_18:48:31_2016_216136.eps.png", "HL_fri_nov_25_18:59:16_2016_966726.eps.png", "HL_fri_nov_25_19:09:39_2016_879734.eps.png", "HL_fri_nov_25_19:20:12_2016_452307.eps.png", "HL_fri_nov_25_19:30:24_2016_072912.eps.png", "HL_fri_nov_25_19:41:01_2016_961595.eps.png", "HL_fri_nov_25_19:51:45_2016_094113.eps.png", "HL_fri_nov_25_20:04:21_2016_851437.eps.png", "HL_fri_nov_25_20:14:38_2016_244804.eps.png", "HL_fri_nov_25_20:25:05_2016_194138.eps.png", "HL_fri_nov_25_20:35:30_2016_706584.eps.png", "HL_fri_nov_25_20:46:18_2016_115551.eps.png", "HL_fri_nov_25_20:56:51_2016_434613.eps.png", "HL_fri_nov_25_21:07:16_2016_345802.eps.png", "HL_fri_nov_25_21:19:39_2016_242295.eps.png", "HL_fri_nov_25_21:29:56_2016_073324.eps.png", "HL_fri_nov_25_21:40:57_2016_912744.eps.png", "HL_fri_nov_25_21:51:21_2016_811232.eps.png", "HL_fri_nov_25_22:02:00_2016_464853.eps.png", "HL_fri_nov_25_22:12:33_2016_311646.eps.png", "HL_fri_nov_25_22:23:06_2016_419059.eps.png", "HL_fri_nov_25_22:33:21_2016_300501.eps.png", "HL_fri_nov_25_22:43:42_2016_578129.eps.png", "HL_fri_nov_25_22:54:12_2016_329028.eps.png", "HL_fri_nov_25_23:04:26_2016_740298.eps.png", "HL_fri_nov_25_23:15:14_2016_256177.eps.png", "HL_fri_nov_25_23:25:58_2016_916026.eps.png", "HL_fri_nov_25_23:36:37_2016_105106.eps.png", "HL_fri_nov_25_23:47:07_2016_939296.eps.png", "HL_fri_nov_25_23:57:19_2016_072604.eps.png", "HL_sat_nov_26_00:07:34_2016_938694.eps.png", "HL_sat_nov_26_00:18:51_2016_373893.eps.png", "HL_sat_nov_26_00:30:12_2016_179003.eps.png", "HL_sat_nov_26_00:41:09_2016_322502.eps.png", "HL_sat_nov_26_00:51:24_2016_672569.eps.png", "HL_sat_nov_26_01:02:44_2016_255697.eps.png", "HL_sat_nov_26_01:13:03_2016_724490.eps.png", "HL_sat_nov_26_01:23:18_2016_026832.eps.png", "HL_sat_nov_26_01:34:57_2016_831493.eps.png", "HL_sat_nov_26_01:45:58_2016_192203.eps.png", "HL_sat_nov_26_01:56:16_2016_804459.eps.png", "HL_sat_nov_26_02:06:36_2016_858523.eps.png", "HL_sat_nov_26_02:16:50_2016_655082.eps.png", "HL_sat_nov_26_02:27:08_2016_857223.eps.png", "HL_sat_nov_26_02:38:04_2016_438484.eps.png", "HL_sat_nov_26_02:49:52_2016_887962.eps.png", "HL_sat_nov_26_03:00:16_2016_902515.eps.png", "HL_sat_nov_26_03:12:51_2016_496125.eps.png", "HL_sat_nov_26_03:24:34_2016_026536.eps.png", "HL_sat_nov_26_03:35:00_2016_657004.eps.png", "HL_sat_nov_26_03:46:26_2016_520036.eps.png", "HL_sat_nov_26_03:57:18_2016_046552.eps.png", "HL_sat_nov_26_04:07:50_2016_890097.eps.png", "HL_sat_nov_26_04:18:04_2016_516449.eps.png", "HL_sat_nov_26_04:28:16_2016_351240.eps.png", "HL_sat_nov_26_04:39:16_2016_989250.eps.png", "HL_sat_nov_26_04:50:18_2016_660016.eps.png", "HL_sat_nov_26_05:00:42_2016_849595.eps.png", "HL_sat_nov_26_05:10:54_2016_769435.eps.png", "HL_sat_nov_26_05:22:22_2016_029131.eps.png", "HL_sat_nov_26_05:32:36_2016_736873.eps.png", "HL_sat_nov_26_05:42:58_2016_330729.eps.png", "HL_sat_nov_26_05:54:02_2016_615680.eps.png", "HL_sat_nov_26_06:05:04_2016_134961.eps.png", "HL_sat_nov_26_06:15:57_2016_240059.eps.png", "HL_sat_nov_26_06:26:21_2016_647349.eps.png", "HL_sat_nov_26_06:37:08_2016_089369.eps.png", "HL_sat_nov_26_06:47:52_2016_174835.eps.png", "HL_sat_nov_26_06:58:30_2016_241191.eps.png", "HL_sat_nov_26_07:08:58_2016_214702.eps.png", "HL_sat_nov_26_07:20:19_2016_246337.eps.png", "HL_sat_nov_26_07:30:38_2016_143436.eps.png", "HL_sat_nov_26_07:41:28_2016_712853.eps.png", "HL_thu_nov_24_17:36:09_2016_444276.eps.png", "HL_thu_nov_24_17:46:23_2016_731160.eps.png", "HL_thu_nov_24_17:57:18_2016_837572.eps.png", "HL_thu_nov_24_18:08:11_2016_892297.eps.png", "HL_thu_nov_24_18:19:42_2016_682931.eps.png", "HL_thu_nov_24_18:30:04_2016_566493.eps.png", "HL_thu_nov_24_18:41:40_2016_671701.eps.png", "HL_thu_nov_24_18:51:55_2016_914777.eps.png", "HL_thu_nov_24_19:03:14_2016_882919.eps.png", "HL_thu_nov_24_19:14:43_2016_437752.eps.png", "HL_thu_nov_24_19:25:08_2016_639806.eps.png", "HL_thu_nov_24_19:36:56_2016_110029.eps.png", "HL_thu_nov_24_19:48:09_2016_553224.eps.png", "HL_thu_nov_24_19:58:21_2016_957621.eps.png", "HL_thu_nov_24_20:08:42_2016_728432.eps.png", "HL_thu_nov_24_20:19:14_2016_537977.eps.png", "HL_thu_nov_24_20:29:26_2016_555749.eps.png", "HL_thu_nov_24_20:39:40_2016_985386.eps.png", "HL_thu_nov_24_20:49:53_2016_928033.eps.png", "HL_thu_nov_24_21:00:29_2016_335098.eps.png", "HL_thu_nov_24_21:11:21_2016_453752.eps.png", "HL_thu_nov_24_21:21:49_2016_404198.eps.png", "HL_thu_nov_24_21:32:22_2016_762083.eps.png", "HL_thu_nov_24_21:44:50_2016_806726.eps.png", "HL_thu_nov_24_21:55:47_2016_394538.eps.png", "HL_thu_nov_24_22:06:37_2016_795404.eps.png", "HL_thu_nov_24_22:17:33_2016_841514.eps.png", "HL_thu_nov_24_22:28:30_2016_456407.eps.png", "HL_thu_nov_24_22:39:00_2016_901124.eps.png", "HL_thu_nov_24_22:49:55_2016_774323.eps.png", "HL_thu_nov_24_23:01:03_2016_418641.eps.png", "HL_thu_nov_24_23:11:42_2016_215622.eps.png", "HL_thu_nov_24_23:22:12_2016_865827.eps.png", "HL_thu_nov_24_23:32:29_2016_042069.eps.png", "HL_thu_nov_24_23:42:38_2016_611111.eps.png", "HL_thu_nov_24_23:52:47_2016_385398.eps.png"] 16 | 17 | # Generate files 18 | 19 | for file_name in file_list: 20 | 21 | file_name = file_name.replace(':', '-') + '.txt' 22 | with open(os.path.join(data_dir, file_name), 'w') as f: 23 | f.write(str(random.random())) 24 | -------------------------------------------------------------------------------- /Lecture 10/AverageImage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cython: AverageImage.pyx 7 | 290 | 297 | 298 | 299 |

Generated by Cython 0.25.2

300 |

301 | Yellow lines hint at Python interaction.
302 | Click on a line that starts with a "+" to see the C code that Cython generated for it. 303 |

304 |

Raw output: AverageImage.c

305 |
 01: 
306 |
 02: 
307 |
+03: # Import cython libraries
308 |
  __pyx_t_2 = PyDict_New(); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 3, __pyx_L1_error)
309 |   __Pyx_GOTREF(__pyx_t_2);
310 |   if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_2) < 0) __PYX_ERR(0, 3, __pyx_L1_error)
311 |   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
312 | 
 04: cimport cython
313 |
+05: import numpy as np
314 |
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
315 |   __Pyx_GOTREF(__pyx_t_1);
316 |   if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) __PYX_ERR(0, 5, __pyx_L1_error)
317 |   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
318 | 
 06: cimport numpy as np
319 |
 07: 
320 |
 08: 
321 |
 09: # Define cython types for numpy arrays
322 |
+10: FLOAT_TYPE = np.float64
323 |
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error)
324 |   __Pyx_GOTREF(__pyx_t_1);
325 |   __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_float64); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 10, __pyx_L1_error)
326 |   __Pyx_GOTREF(__pyx_t_2);
327 |   __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
328 |   if (PyDict_SetItem(__pyx_d, __pyx_n_s_FLOAT_TYPE, __pyx_t_2) < 0) __PYX_ERR(0, 10, __pyx_L1_error)
329 |   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
330 | 
 11: ctypedef np.float64_t FLOAT_TYPE_t
331 |
 12: 
332 |
 13: 
333 |
 14: # @cython.boundscheck(False) # This disables checking that the indices of an array are valid
334 |
 15: # @cython.wraparound(False) # This disables negative indexing, e.g. array[-1]
335 |
 16: # @cython.cdivision(True) # This disables checks for divisions by zero
336 |
+17: def cy_average_img(np.ndarray[FLOAT_TYPE_t, ndim=2] img, int region):
337 |
/* Python wrapper */
338 | static PyObject *__pyx_pw_12AverageImage_1cy_average_img(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
339 | static char __pyx_doc_12AverageImage_cy_average_img[] = " Averages image pixels in (region)x(region) neighbourhood.\n    \n    Arguments:\n        img: [2D ndarray] image as numpy array\n        region: [int] averaging neighbourhood, should be an odd number (3, 5, 7, 9, etc.)\n    \n    Return:\n        img_avg: [2D ndarray] averaged image\n    ";
340 | static PyMethodDef __pyx_mdef_12AverageImage_1cy_average_img = {"cy_average_img", (PyCFunction)__pyx_pw_12AverageImage_1cy_average_img, METH_VARARGS|METH_KEYWORDS, __pyx_doc_12AverageImage_cy_average_img};
341 | static PyObject *__pyx_pw_12AverageImage_1cy_average_img(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
342 |   PyArrayObject *__pyx_v_img = 0;
343 |   int __pyx_v_region;
344 |   PyObject *__pyx_r = 0;
345 |   __Pyx_RefNannyDeclarations
346 |   __Pyx_RefNannySetupContext("cy_average_img (wrapper)", 0);
347 |   {
348 |     static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_img,&__pyx_n_s_region,0};
349 |     PyObject* values[2] = {0,0};
350 |     if (unlikely(__pyx_kwds)) {
351 |       Py_ssize_t kw_args;
352 |       const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
353 |       switch (pos_args) {
354 |         case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
355 |         case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
356 |         case  0: break;
357 |         default: goto __pyx_L5_argtuple_error;
358 |       }
359 |       kw_args = PyDict_Size(__pyx_kwds);
360 |       switch (pos_args) {
361 |         case  0:
362 |         if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_img)) != 0)) kw_args--;
363 |         else goto __pyx_L5_argtuple_error;
364 |         case  1:
365 |         if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_region)) != 0)) kw_args--;
366 |         else {
367 |           __Pyx_RaiseArgtupleInvalid("cy_average_img", 1, 2, 2, 1); __PYX_ERR(0, 17, __pyx_L3_error)
368 |         }
369 |       }
370 |       if (unlikely(kw_args > 0)) {
371 |         if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cy_average_img") < 0)) __PYX_ERR(0, 17, __pyx_L3_error)
372 |       }
373 |     } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
374 |       goto __pyx_L5_argtuple_error;
375 |     } else {
376 |       values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
377 |       values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
378 |     }
379 |     __pyx_v_img = ((PyArrayObject *)values[0]);
380 |     __pyx_v_region = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_region == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 17, __pyx_L3_error)
381 |   }
382 |   goto __pyx_L4_argument_unpacking_done;
383 |   __pyx_L5_argtuple_error:;
384 |   __Pyx_RaiseArgtupleInvalid("cy_average_img", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 17, __pyx_L3_error)
385 |   __pyx_L3_error:;
386 |   __Pyx_AddTraceback("AverageImage.cy_average_img", __pyx_clineno, __pyx_lineno, __pyx_filename);
387 |   __Pyx_RefNannyFinishContext();
388 |   return NULL;
389 |   __pyx_L4_argument_unpacking_done:;
390 |   if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_img), __pyx_ptype_5numpy_ndarray, 1, "img", 0))) __PYX_ERR(0, 17, __pyx_L1_error)
391 |   __pyx_r = __pyx_pf_12AverageImage_cy_average_img(__pyx_self, __pyx_v_img, __pyx_v_region);
392 | 
393 |   /* function exit code */
394 |   goto __pyx_L0;
395 |   __pyx_L1_error:;
396 |   __pyx_r = NULL;
397 |   __pyx_L0:;
398 |   __Pyx_RefNannyFinishContext();
399 |   return __pyx_r;
400 | }
401 | 
402 | static PyObject *__pyx_pf_12AverageImage_cy_average_img(CYTHON_UNUSED PyObject *__pyx_self, PyArrayObject *__pyx_v_img, int __pyx_v_region) {
403 |   int __pyx_v_reg_r;
404 |   PyArrayObject *__pyx_v_img_avg = 0;
405 |   int __pyx_v_x_size;
406 |   int __pyx_v_y_size;
407 |   double __pyx_v_s;
408 |   int __pyx_v_i;
409 |   int __pyx_v_j;
410 |   int __pyx_v_m;
411 |   int __pyx_v_n;
412 |   int __pyx_v_x;
413 |   int __pyx_v_y;
414 |   __Pyx_LocalBuf_ND __pyx_pybuffernd_img;
415 |   __Pyx_Buffer __pyx_pybuffer_img;
416 |   __Pyx_LocalBuf_ND __pyx_pybuffernd_img_avg;
417 |   __Pyx_Buffer __pyx_pybuffer_img_avg;
418 |   PyObject *__pyx_r = NULL;
419 |   __Pyx_RefNannyDeclarations
420 |   __Pyx_RefNannySetupContext("cy_average_img", 0);
421 |   __pyx_pybuffer_img_avg.pybuffer.buf = NULL;
422 |   __pyx_pybuffer_img_avg.refcount = 0;
423 |   __pyx_pybuffernd_img_avg.data = NULL;
424 |   __pyx_pybuffernd_img_avg.rcbuffer = &__pyx_pybuffer_img_avg;
425 |   __pyx_pybuffer_img.pybuffer.buf = NULL;
426 |   __pyx_pybuffer_img.refcount = 0;
427 |   __pyx_pybuffernd_img.data = NULL;
428 |   __pyx_pybuffernd_img.rcbuffer = &__pyx_pybuffer_img;
429 |   {
430 |     __Pyx_BufFmt_StackElem __pyx_stack[1];
431 |     if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_img.rcbuffer->pybuffer, (PyObject*)__pyx_v_img, &__Pyx_TypeInfo_nn___pyx_t_12AverageImage_FLOAT_TYPE_t, PyBUF_FORMAT| PyBUF_STRIDES, 2, 0, __pyx_stack) == -1)) __PYX_ERR(0, 17, __pyx_L1_error)
432 |   }
433 |   __pyx_pybuffernd_img.diminfo[0].strides = __pyx_pybuffernd_img.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_img.diminfo[0].shape = __pyx_pybuffernd_img.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_img.diminfo[1].strides = __pyx_pybuffernd_img.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_img.diminfo[1].shape = __pyx_pybuffernd_img.rcbuffer->pybuffer.shape[1];
434 | /* … */
435 |   /* function exit code */
436 |   __pyx_L1_error:;
437 |   __Pyx_XDECREF(__pyx_t_1);
438 |   __Pyx_XDECREF(__pyx_t_2);
439 |   __Pyx_XDECREF(__pyx_t_3);
440 |   __Pyx_XDECREF(__pyx_t_4);
441 |   { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
442 |     __Pyx_PyThreadState_declare
443 |     __Pyx_PyThreadState_assign
444 |     __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
445 |     __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_img.rcbuffer->pybuffer);
446 |     __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_img_avg.rcbuffer->pybuffer);
447 |   __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
448 |   __Pyx_AddTraceback("AverageImage.cy_average_img", __pyx_clineno, __pyx_lineno, __pyx_filename);
449 |   __pyx_r = NULL;
450 |   goto __pyx_L2;
451 |   __pyx_L0:;
452 |   __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_img.rcbuffer->pybuffer);
453 |   __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_img_avg.rcbuffer->pybuffer);
454 |   __pyx_L2:;
455 |   __Pyx_XDECREF((PyObject *)__pyx_v_img_avg);
456 |   __Pyx_XGIVEREF(__pyx_r);
457 |   __Pyx_RefNannyFinishContext();
458 |   return __pyx_r;
459 | }
460 | /* … */
461 |   __pyx_tuple__10 = PyTuple_Pack(13, __pyx_n_s_img, __pyx_n_s_region, __pyx_n_s_reg_r, __pyx_n_s_img_avg, __pyx_n_s_x_size, __pyx_n_s_y_size, __pyx_n_s_s, __pyx_n_s_i, __pyx_n_s_j, __pyx_n_s_m, __pyx_n_s_n, __pyx_n_s_x, __pyx_n_s_y); if (unlikely(!__pyx_tuple__10)) __PYX_ERR(0, 17, __pyx_L1_error)
462 |   __Pyx_GOTREF(__pyx_tuple__10);
463 |   __Pyx_GIVEREF(__pyx_tuple__10);
464 | /* … */
465 |   __pyx_t_2 = PyCFunction_NewEx(&__pyx_mdef_12AverageImage_1cy_average_img, NULL, __pyx_n_s_AverageImage); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 17, __pyx_L1_error)
466 |   __Pyx_GOTREF(__pyx_t_2);
467 |   if (PyDict_SetItem(__pyx_d, __pyx_n_s_cy_average_img, __pyx_t_2) < 0) __PYX_ERR(0, 17, __pyx_L1_error)
468 |   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
469 | 
 18:     """ Averages image pixels in (region)x(region) neighbourhood.
470 |
 19: 
471 |
 20:     Arguments:
472 |
 21:         img: [2D ndarray] image as numpy array
473 |
 22:         region: [int] averaging neighbourhood, should be an odd number (3, 5, 7, 9, etc.)
474 |
 23: 
475 |
 24:     Return:
476 |
 25:         img_avg: [2D ndarray] averaged image
477 |
 26:     """
478 |
 27: 
479 |
+28:     cdef int reg_r = region//2
480 |
  __pyx_v_reg_r = __Pyx_div_long(__pyx_v_region, 2);
481 | 
 29: 
482 |
 30:     # Output image
483 |
+31:     cdef np.ndarray[FLOAT_TYPE_t, ndim=2] img_avg = np.zeros_like(img)
484 |
  __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 31, __pyx_L1_error)
485 |   __Pyx_GOTREF(__pyx_t_2);
486 |   __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_zeros_like); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 31, __pyx_L1_error)
487 |   __Pyx_GOTREF(__pyx_t_3);
488 |   __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
489 |   __pyx_t_2 = NULL;
490 |   if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_3))) {
491 |     __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
492 |     if (likely(__pyx_t_2)) {
493 |       PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3);
494 |       __Pyx_INCREF(__pyx_t_2);
495 |       __Pyx_INCREF(function);
496 |       __Pyx_DECREF_SET(__pyx_t_3, function);
497 |     }
498 |   }
499 |   if (!__pyx_t_2) {
500 |     __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_3, ((PyObject *)__pyx_v_img)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 31, __pyx_L1_error)
501 |     __Pyx_GOTREF(__pyx_t_1);
502 |   } else {
503 |     #if CYTHON_FAST_PYCALL
504 |     if (PyFunction_Check(__pyx_t_3)) {
505 |       PyObject *__pyx_temp[2] = {__pyx_t_2, ((PyObject *)__pyx_v_img)};
506 |       __pyx_t_1 = __Pyx_PyFunction_FastCall(__pyx_t_3, __pyx_temp+1-1, 1+1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 31, __pyx_L1_error)
507 |       __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
508 |       __Pyx_GOTREF(__pyx_t_1);
509 |     } else
510 |     #endif
511 |     #if CYTHON_FAST_PYCCALL
512 |     if (__Pyx_PyFastCFunction_Check(__pyx_t_3)) {
513 |       PyObject *__pyx_temp[2] = {__pyx_t_2, ((PyObject *)__pyx_v_img)};
514 |       __pyx_t_1 = __Pyx_PyCFunction_FastCall(__pyx_t_3, __pyx_temp+1-1, 1+1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 31, __pyx_L1_error)
515 |       __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
516 |       __Pyx_GOTREF(__pyx_t_1);
517 |     } else
518 |     #endif
519 |     {
520 |       __pyx_t_4 = PyTuple_New(1+1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 31, __pyx_L1_error)
521 |       __Pyx_GOTREF(__pyx_t_4);
522 |       __Pyx_GIVEREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_2); __pyx_t_2 = NULL;
523 |       __Pyx_INCREF(((PyObject *)__pyx_v_img));
524 |       __Pyx_GIVEREF(((PyObject *)__pyx_v_img));
525 |       PyTuple_SET_ITEM(__pyx_t_4, 0+1, ((PyObject *)__pyx_v_img));
526 |       __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_4, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 31, __pyx_L1_error)
527 |       __Pyx_GOTREF(__pyx_t_1);
528 |       __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
529 |     }
530 |   }
531 |   __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
532 |   if (!(likely(((__pyx_t_1) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_1, __pyx_ptype_5numpy_ndarray))))) __PYX_ERR(0, 31, __pyx_L1_error)
533 |   __pyx_t_5 = ((PyArrayObject *)__pyx_t_1);
534 |   {
535 |     __Pyx_BufFmt_StackElem __pyx_stack[1];
536 |     if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_img_avg.rcbuffer->pybuffer, (PyObject*)__pyx_t_5, &__Pyx_TypeInfo_nn___pyx_t_12AverageImage_FLOAT_TYPE_t, PyBUF_FORMAT| PyBUF_STRIDES| PyBUF_WRITABLE, 2, 0, __pyx_stack) == -1)) {
537 |       __pyx_v_img_avg = ((PyArrayObject *)Py_None); __Pyx_INCREF(Py_None); __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.buf = NULL;
538 |       __PYX_ERR(0, 31, __pyx_L1_error)
539 |     } else {__pyx_pybuffernd_img_avg.diminfo[0].strides = __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_img_avg.diminfo[0].shape = __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_img_avg.diminfo[1].strides = __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_img_avg.diminfo[1].shape = __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.shape[1];
540 |     }
541 |   }
542 |   __pyx_t_5 = 0;
543 |   __pyx_v_img_avg = ((PyArrayObject *)__pyx_t_1);
544 |   __pyx_t_1 = 0;
545 | 
 32: 
546 |
+33:     cdef int x_size = img.shape[0]
547 |
  __pyx_v_x_size = (__pyx_v_img->dimensions[0]);
548 | 
+34:     cdef int y_size = img.shape[1]
549 |
  __pyx_v_y_size = (__pyx_v_img->dimensions[1]);
550 | 
 35: 
551 |
+36:     cdef double s = 0
552 |
  __pyx_v_s = 0.0;
553 | 
 37:     cdef int i, j, m, n, x, y
554 |
 38: 
555 |
 39:     # Average pixels in the 3x3 region
556 |
+40:     for i in range(x_size):
557 |
  __pyx_t_6 = __pyx_v_x_size;
558 |   for (__pyx_t_7 = 0; __pyx_t_7 < __pyx_t_6; __pyx_t_7+=1) {
559 |     __pyx_v_i = __pyx_t_7;
560 | 
+41:         for j in range(y_size):
561 |
    __pyx_t_8 = __pyx_v_y_size;
562 |     for (__pyx_t_9 = 0; __pyx_t_9 < __pyx_t_8; __pyx_t_9+=1) {
563 |       __pyx_v_j = __pyx_t_9;
564 | 
 42: 
565 |
+43:             s = 0
566 |
      __pyx_v_s = 0.0;
567 | 
+44:             for x in range(-reg_r, reg_r + 1):
568 |
      __pyx_t_10 = (__pyx_v_reg_r + 1);
569 |       for (__pyx_t_11 = (-__pyx_v_reg_r); __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {
570 |         __pyx_v_x = __pyx_t_11;
571 | 
+45:                 for y in range(-reg_r, reg_r + 1):
572 |
        __pyx_t_12 = (__pyx_v_reg_r + 1);
573 |         for (__pyx_t_13 = (-__pyx_v_reg_r); __pyx_t_13 < __pyx_t_12; __pyx_t_13+=1) {
574 |           __pyx_v_y = __pyx_t_13;
575 | 
 46: 
576 |
+47:                     m = i + x
577 |
          __pyx_v_m = (__pyx_v_i + __pyx_v_x);
578 | 
+48:                     n = j + y
579 |
          __pyx_v_n = (__pyx_v_j + __pyx_v_y);
580 | 
 49: 
581 |
 50:                     # Wrap the borders
582 |
+51:                     if m >= x_size:
583 |
          __pyx_t_14 = ((__pyx_v_m >= __pyx_v_x_size) != 0);
584 |           if (__pyx_t_14) {
585 | /* … */
586 |           }
587 | 
+52:                         m = m%x_size
588 |
            if (unlikely(__pyx_v_x_size == 0)) {
589 |               PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero");
590 |               __PYX_ERR(0, 52, __pyx_L1_error)
591 |             }
592 |             __pyx_v_m = __Pyx_mod_int(__pyx_v_m, __pyx_v_x_size);
593 | 
 53: 
594 |
+54:                     if n >= y_size:
595 |
          __pyx_t_14 = ((__pyx_v_n >= __pyx_v_y_size) != 0);
596 |           if (__pyx_t_14) {
597 | /* … */
598 |           }
599 | 
+55:                         n = n%y_size
600 |
            if (unlikely(__pyx_v_y_size == 0)) {
601 |               PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero");
602 |               __PYX_ERR(0, 55, __pyx_L1_error)
603 |             }
604 |             __pyx_v_n = __Pyx_mod_int(__pyx_v_n, __pyx_v_y_size);
605 | 
 56: 
606 |
 57: 
607 |
+58:                     s += img[m, n]
608 |
          __pyx_t_15 = __pyx_v_m;
609 |           __pyx_t_16 = __pyx_v_n;
610 |           __pyx_t_17 = -1;
611 |           if (__pyx_t_15 < 0) {
612 |             __pyx_t_15 += __pyx_pybuffernd_img.diminfo[0].shape;
613 |             if (unlikely(__pyx_t_15 < 0)) __pyx_t_17 = 0;
614 |           } else if (unlikely(__pyx_t_15 >= __pyx_pybuffernd_img.diminfo[0].shape)) __pyx_t_17 = 0;
615 |           if (__pyx_t_16 < 0) {
616 |             __pyx_t_16 += __pyx_pybuffernd_img.diminfo[1].shape;
617 |             if (unlikely(__pyx_t_16 < 0)) __pyx_t_17 = 1;
618 |           } else if (unlikely(__pyx_t_16 >= __pyx_pybuffernd_img.diminfo[1].shape)) __pyx_t_17 = 1;
619 |           if (unlikely(__pyx_t_17 != -1)) {
620 |             __Pyx_RaiseBufferIndexError(__pyx_t_17);
621 |             __PYX_ERR(0, 58, __pyx_L1_error)
622 |           }
623 |           __pyx_v_s = (__pyx_v_s + (*__Pyx_BufPtrStrided2d(__pyx_t_12AverageImage_FLOAT_TYPE_t *, __pyx_pybuffernd_img.rcbuffer->pybuffer.buf, __pyx_t_15, __pyx_pybuffernd_img.diminfo[0].strides, __pyx_t_16, __pyx_pybuffernd_img.diminfo[1].strides)));
624 |         }
625 |       }
626 | 
 59: 
627 |
 60: 
628 |
+61:             img_avg[i, j] = s/(region**2)
629 |
      __pyx_t_10 = __Pyx_pow_long(((long)__pyx_v_region), 2);
630 |       if (unlikely(__pyx_t_10 == 0)) {
631 |         PyErr_SetString(PyExc_ZeroDivisionError, "float division");
632 |         __PYX_ERR(0, 61, __pyx_L1_error)
633 |       }
634 |       __pyx_t_18 = __pyx_v_i;
635 |       __pyx_t_19 = __pyx_v_j;
636 |       __pyx_t_11 = -1;
637 |       if (__pyx_t_18 < 0) {
638 |         __pyx_t_18 += __pyx_pybuffernd_img_avg.diminfo[0].shape;
639 |         if (unlikely(__pyx_t_18 < 0)) __pyx_t_11 = 0;
640 |       } else if (unlikely(__pyx_t_18 >= __pyx_pybuffernd_img_avg.diminfo[0].shape)) __pyx_t_11 = 0;
641 |       if (__pyx_t_19 < 0) {
642 |         __pyx_t_19 += __pyx_pybuffernd_img_avg.diminfo[1].shape;
643 |         if (unlikely(__pyx_t_19 < 0)) __pyx_t_11 = 1;
644 |       } else if (unlikely(__pyx_t_19 >= __pyx_pybuffernd_img_avg.diminfo[1].shape)) __pyx_t_11 = 1;
645 |       if (unlikely(__pyx_t_11 != -1)) {
646 |         __Pyx_RaiseBufferIndexError(__pyx_t_11);
647 |         __PYX_ERR(0, 61, __pyx_L1_error)
648 |       }
649 |       *__Pyx_BufPtrStrided2d(__pyx_t_12AverageImage_FLOAT_TYPE_t *, __pyx_pybuffernd_img_avg.rcbuffer->pybuffer.buf, __pyx_t_18, __pyx_pybuffernd_img_avg.diminfo[0].strides, __pyx_t_19, __pyx_pybuffernd_img_avg.diminfo[1].strides) = (__pyx_v_s / __pyx_t_10);
650 |     }
651 |   }
652 | 
 62: 
653 |
 63: 
654 |
+64:     return img_avg
655 |
  __Pyx_XDECREF(__pyx_r);
656 |   __Pyx_INCREF(((PyObject *)__pyx_v_img_avg));
657 |   __pyx_r = ((PyObject *)__pyx_v_img_avg);
658 |   goto __pyx_L0;
659 | 
 65: 
660 |
 66: 
661 |
 67: 
662 |
 68: # Import the square root function from a C library
663 |
 69: from libc.math cimport sqrt
664 |
 70: 
665 |
 71: 
666 |
+72: cdef double euclidDist(double x1, double y1, double x2, double y2):
667 |
static double __pyx_f_12AverageImage_euclidDist(double __pyx_v_x1, double __pyx_v_y1, double __pyx_v_x2, double __pyx_v_y2) {
668 |   double __pyx_r;
669 |   __Pyx_RefNannyDeclarations
670 |   __Pyx_RefNannySetupContext("euclidDist", 0);
671 | /* … */
672 |   /* function exit code */
673 |   __pyx_L0:;
674 |   __Pyx_RefNannyFinishContext();
675 |   return __pyx_r;
676 | }
677 | 
 73:     """ Calculate the Euclidian distance of two points in 2D. """
678 |
+74:     return sqrt((x1 - x2)**2 + (y1 - y2)**2)
679 |
  __pyx_r = sqrt((pow((__pyx_v_x1 - __pyx_v_x2), 2.0) + pow((__pyx_v_y1 - __pyx_v_y2), 2.0)));
680 |   goto __pyx_L0;
681 | 
682 | --------------------------------------------------------------------------------