#!/usr/bin/env python # coding: utf-8 # Astronomical data analysis using Python # ==================================== # # Lecture 3 # --------------------- # # # Our first program - introduced in the previous lecture # ------------------------------------------ # In[126]: a = 3 b = 5 c = a+b d = a-b q, r = a/b, a%b # Yes, this is allowed! # Now, let's print! print ("Hello World!") # just for fun print ("Sum, Difference = ", c, d) print ("Quotient and Remainder = ", q, r) # Our First Program - Rewritten! # ------------------------------ # # Let us introduce the following modifications to the program. # # * We use floats instead of ints. # * We accept the numbers from the user instead of "hard coding" them. # In[65]: # Modified first program. a = input("Please enter number 1: ") b = input("Please enter number 2: ") c, d = a+b, a-b q, i = a/b, a//b print (c,d,q,i) # What happened? # -------------- # # * Anything input through the keyboard using input() is ... a "string". # * Strings support addition (concatenation) but nothing else. # # So what should we do? # --------------- # # * "3.0" is a string. 3.0 is a float! # * To convert "3.0" into a float, we use a simple function - float("3.0") # # So, let's rewrite our program! # In[127]: a = float( input("Enter Number 1: ") ) # We are nesting functions here. b = float( input("Enter Number 2: ") ) c,d = a+b, a-b q,i = a/b, a//b # a//b is the floor division operator print ("Addition = %f, Difference = %f " % (c,d)) print ("Quotient = %f, Floor division quotient = %f" % (q,i)) # The output looks ugly. Wish I could control the number of decimal places... # In[128]: a = float( input("Enter Number 1: ") ) b = float( input("Enter Number 2: ") ) c,d = a+b, a-b q,i = a/b, a//b print("Addition = %.2f, Difference = %.2f " % (c,d)) print("Quotient = %.2f, Floor division quotient = %.2f" % (q,i)) # Ah! now, that's much better. # String Formatting # ---------- # # We have seen a powerful of constructing strings in the previous example. # In[68]: print ("Addition = %.2f, Difference = %.2f " % (c,d)) # C / FORTRAN users amongst you will immediately understand this method of string construction. # # Python supports this and its own way of string formatting. # ------- # In[69]: gal_name = "NGC 7709"; int_bmagnitude = 13.6 # In[70]: statement1 = "The galaxy %s has an integrated B-band magnitude of %.2f" % (gal_name, int_bmagnitude) # In[71]: statement2 = "The galaxy {0:s} has an integrated B-band magnitude of {1:.2f}".format(gal_name, int_bmagnitude) # In[72]: statement3 = "The galaxy {name:s} has an integrated B-band magnitude of {mag:.2f}".format(name=gal_name, mag=int_bmagnitude) # All the above statements are equivalent! # ------ # In[73]: print (statement1,"\n", statement2, "\n", statement3, "\n") # You can choose whichever method you like! # # As a former C/C++ user, I tend to use the first method. # # But ... second and third methods are more "Pythonic". If you don't have previous experience with the C type formatting use one of the more Pythonic ways. # Raw Strings # ------------ # # * We have seen the three methods of string declaration. # * We have also seen string formatting. # * String formatting taught us that symbols like { or % have special meanings in Python. # In[74]: # There is a special way of declaring strings where # we can ask Python to ignore all these symbols. raw_string = r"Here is a percentage sign % and a brace }" print (raw_string) # Usefulness of Raw Strings - Example # ------- # # * Typically, when we make plots and set labels, we would like to invoke a LaTeX parser. # * This would involve a lot \ $ and {}. # # In such cases, it's best to use raw strings. # # plt.xlabel(r" \log \rm{F}_v") # # Other examples with special characters include writing Windows file paths, XML code, HTML code, etc. # Conditionals # ------ # In[75]: num = int(input("Enter number: ") ) if num %2 == 0: print ("%d is even!" % num) else: print ("%d is odd!" % num) # You will use conditionals a lot in your data analysis. # In[1]: model_choice = int(input( "Enter choice [1 or 2]: ") ) spectrum = 3 # In a realistic case, this will be some complicated object. if model_choice == 1: #model1(spectrum) print ("Model 1 fitted.") elif model_choice == 2: #model2(spectrum) print ("Model 2 fitted.") else: print ("Invalid model entered.") # What do you notice apart from the syntax in the above example? # ------ # Indentation - A Vital Part of the Pythonic Way # -------------- # # Be it the if-block illustrated above or the loops or the functions (to be introduced soon), indentation is at the heart of the Python's way of delimiting blocks! # # Function definitions, loops, if-blocks - nothing has your typical boundaries like { } as in C/C++/Java. # # The "level of the indentation" in the only way to define the scope of a "block". # In support of indentation # ------ # # Look at the following C-like code. # # if (x>0) # if (y>0) # print "Woohoo!" # else # print "Booboo!" # # Which "if" does the "else" belong to? # # In C and C-like languages, the braces {}s do the marking, the indentation is purely optional. In Python, indentation levels determine scopes. In Python the "the else" belongs to "if (x>0)". # # Python forces you to write clean code! (Obfuscation lovers, go take a jump!) # # Use either spaces or tabs (don't ever ever mix them) and use them consistently. I strongly recommend using 4 spaces for each level of indentation. # Wrapping up if-elif-else # ------- # # The general syntax: # # if : # do this # and this # elif : # this # and this # ... # else: # do this # and this # # Conditions are anything that return True or False. # ------ # # * == (equal to) # * != # * \> # * \>= # * < # * <= # # You can combine conditionals using "logical operators" # # * and # * or # * not # The Boolean Data Type # -------------- # In[5]: a = True b = False if a: print ("This comes on screen.") if b: print ("This won't come on screen.") if a or b: print ("This also comes on screen.") if a and b: print ("This won't come on screen either.") # In[78]: type(a) # To check type of object. # Almost all other types have a Boolean Equivalent # ------ # In[6]: a = 1 b = 0 if a: print ("Hello!") if b: print ("Oh No!") type(a) # In[80]: s1 = ""; s2 = "Hello" # s1 is an empty string if s1: print ("Won't be printed.") if s2: print ("Will be printed.") # This is bad Python style because remember that explicit is better than implicit. Use an expression that evaluates to a boolean instead. Keep your programs readable. # Conditional Expression # ------- # # Consider... # In[81]: if 5 > 6: x = 2 else: x = 3 # In[82]: y = 2 if 5 > 6 else 3 # if else block in one line is allowed # In[83]: print (x,y) # A Second Plunge into the Data Types # =========== # # The two other data types we need to know: # # * Lists # * Dictionaries # # Data Types I will not cover (formally): # # * Tuples (immutable lists!) # * Sets (key-less dictionaries!) # * Complex Numbers # * Fractions # * Decimals # Lists # ----- # In[84]: a = [1,2,3,4] # simple ordered collection # In[85]: b = ["Hello", 45, 7.64, True] # can be heterogeneous # In[86]: a[0], a[-1], a[1:3] # All "sequence" operations supported. # In[87]: b[0][1] # 2nd member of the 1st member # In[88]: a = [ [1,2,3] , [4,5,6] , [7,8,9] ] # list of lists allowed. # In[89]: a[2][1] # Accessing elements in nested structures. # In[90]: [1,3,4] + [5,6,7] # Support concatenation # In[91]: [1,6,8] * 3 # Repetition (like strings) # Lists are Mutable! (Strings are not!) # ---- # In[9]: a = [1,4,5,7] # In[10]: print (a) # In[11]: a[2] = 777 # set third element to 777 # In[12]: print (a) # List Methods # ---- # In[13]: a = [1,3,5] print (a) # In[97]: a.append(7) # adds an element to the end print (a) # the list has changed (unlike string methods!) # In[98]: a.extend([9,11,13]) # concatenates a list at the end print (a) # In[99]: a.pop() # Removes one element at the end. print (a) # In[100]: a.pop(2) # Removes 3rd element. print (a) # Don't Forget!!! # ----- # # In[101]: print (dir(a)) # list of methods for a list "a" # In[102]: help(a.sort) # Implications of Mutability # ---- # In[103]: l = [1,2,3,4] m = l l.append(5) print (l) print (m) # l and m point to the same object. When the object mutates, whether you refer to it using l or m, you get the same mutated object. # How do I make a copy then? # --- # In[104]: l = [1,2,3,4] m = l[:] l.append(5) print (l) print (m) # Python has a module called "copy" available for making copies. Refer to the standard library documentation for details. # Dictionaries # ---- # # * Imagine a list as a collection of objects obj0, obj1, obj2 ... # * First object has a location 0, second 1 ... # * Now, imagine renaming location 0 as "something", location 1 as "somethingelse" ... # * Earlier, you accessed objects at numbered locations a[0]. # * Now, you access objects by specifying location labels a["something"] # # Let's see this at work. # In[105]: d1 = { "a" : 3, "b" : 5} print (d1["a"]) print (d1["b"]) # "a", "b" are called keys and 3,5 are called values. So formally, a dictionary is a collection of key-value pairs. # In[106]: d1["c"] = 7 # Since "c" does not exist, a new key-value pair is made. d1["a"] = 1 # Since "a" exists already, its value is modified. print (d1) # the ordering of key-value pairs in the dictionary is not guaranteed. # Dictionary Methods # ---- # In[107]: keys = d1.keys() # Returns a list of all keys which is stored in "keys". print (keys) # In[108]: values = d1.values() # Returns a list of values. print (values) # In[109]: d1.items() # List of Tuples of key-value pairs. # Defining Dictionaries - ways to do this # ----- # In[110]: d1 = {"a":3, "b":5, "c":7} # we've seen this. # In[111]: keys = ["a", "b", "c"] values = [3,5,7] d2 = dict( zip(keys,values) ) # creates dictionary similar to d1 # In[112]: d3 = dict( a=3, b=5, c=7) # again, same as d1,d2 # In[14]: d4 = dict( [ ("a",3), ("b",5), ("c",7) ] ) # same as d1,d2,d3 # Dictionaries in data analysis # =============== # # z['M31'] # # rmag['M1'] # # lumdist['3C 273'] # # # The while loop # ------- # In[114]: x = 0 while x<5: print (x) x += 1 # In[115]: x = 1 while True: # Without the break statement this loop is infinite print ("x = %d" % x) choice = input("Do you want to continue? ") if choice != "y": break # This statement breaks the loop. else: x += 1 # The "for" loop - Pay Attention! # ----- # # In[116]: x = [5,6,7,8,9,0] # a simple list for i in x: print (i) # In " for i in x", x can be many different things, any iterable object is allowed. # In[117]: s = "Hello!" for c in s: print (c) # No No No! I want my good old for-loop back which generates numbers x to y in steps of z!!! # In[118]: # OKAY!!! Let's try something here. for i in range(2,15,3): print (i) # Let us see some wicked for-loops. # In[17]: a = [1,2,3,4,5] b = "Hello" c = zip(a,b) print(type(c)) for i,j in c: print (i, j) # In[133]: a = "Hello!" for i, c in enumerate(a): print ("Character no. %d is %s" % (i+1, c)) # i+1 is used because Python is 0-indexed. print() help(enumerate) # You can break and continue for-loops too! # In[18]: for i in range(10000): if i%2 == 0: # Even print (i,"is Even") continue print (i, "is Odd") if i == 7: # What if I had said "i==8 or i==10" ?????? break # Traversing Dictionaries using for-loops # ------ # In[136]: d = dict( a = 1, b = 2, c = 3, d = 4) for key,value in d.items(): print (key, "-->", value) # In[123]: for key in d.keys(): print (key, "-->", d[key]) # Style Guide for Python Code # ---------- # # The PEP 8 provides excellent guidance on what to do and what to avoid while writing Python code. I strongly urge you to study PEP 8 carefully and implement its recommendations strictly. # # # https://www.python.org/dev/peps/pep-0008/ # # The PEP 8 is very terse. For a more expanded explanation see: # # # https://realpython.com/python-pep8/ # Python is fully Unicode UTF-8 standard compliant # ===== # In[3]: print("What is your name?") print("আপনার নাম কি?") print("உங்கள் பெயர் என்ன?") print("तुझं नाव काय आहे?") print("तुम्हारा नाम क्या हे?") # Google provides the Cloud Translation API client library for Python # # https://cloud.google.com/translate/docs/reference/libraries/v2/python