#!/usr/bin/env python
# coding: utf-8

# # Astronomical data analysis using Python
# 
# 
# ## Lecture 2, second lecture of the course
# 
# 

# Assumptions!!!
# ================
# 
#  
# 
# You are new to Python! You may or may not have programming experience in another language.
# -----------------------

# Our First Program!
# ===================

# In[4]:


a = 2
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)


# What can we learn from this simple program?
# -----

# Dynamic Typing
# --------------
# 
# * We do not declare variables and types in advance. (dynamic typing)
# * Variables created when first assigned values.
# * Variables do not exist if not assigned. (strong typing)
# 
# Commenting
# ----------
# 
# Everything after # is a comment and is ignored. Comment freely. A comment can be on its own line or following a line of code.
# 
# "print" statement
# ------------------
# 
# Used to print output of any kind. We will use this built-in function of Python often.
# 
# Tuple unpacking assignments
# ----------------------------
#  a,b = 5,6
# 
# 

# Other Things
# ==================
# 
# * Behavior of / and % operators with integer types. (changed in Python 3)
# * No termination symbols at end of Python statements.
# * Exception to the above...
# 
#     a = 3; b = 5
#     

# Under the Hood
# ===================
# 
# * No explicit compiling/linking step. Just run...
#     $ python First.py
# * Internally, program translated into bytecode (.pyc files)
# * The "translation + execution" happens line-by-line
# 
# Implications of "line-by-line" style
# -------------------------------------
# 
# * N lines will be executed before error on N+1th line halts program!
# * An interactive shell.
# * Interpreted language codes are generally slower than equivalent code in compiled languages.

# The First Tour of the Data Types
# ================================
# 
# * Numbers - Integers
# * Numbers - Floats
# 
# Exploration of math module
# 
# * Strings 
# 
# Methods of Declaring Strings
# 
# Concept of Sequences
# 
# Concept of Slicing
# 
# Concept of Mutability
# 
# Introduction of Object.Method concepts

# Integers
# ---------

# In[5]:


8 ** 2 # Exponentiation


# In[6]:


23**200 # Auto-upgrade to "LONG INT" Notice the L!


# In[7]:


5 / 2, 5%2 # Quotient-Remainder Revisited.


# Notice that Python is an effective scientific calculator!

# Floats
# -------
# 

# In[8]:


5.0 * 2, 5*2.0 # Values upgraded to "higher data type".


# In[9]:


5**0.5 # Yes, it works! Square-root.


# In[10]:


5 / 4.0 # No longer a quotient.


# In[11]:


5 % 4.0, 5 % 4.1 # Remainder, yes!!!


# Math Module
# ------------
# 
# * A module can be thought of as a collection of related functions.
# * To use a module, 
# 
#     import ModuleName
#     
# * To use a function inside a module, simply say
# 
#     ModuleName.Function(inputs)
#     
# Let's see the math module in action!

# In[12]:


import math
x = 60*math.pi/180.0
math.sin(x)


# In[13]:


math.sin( math.radians(60) ) # nested functions


# There are about 42 functions inside Math library! So, where can one get a quick reference of what these functions are, what they do and how to use them!?!?

# In[14]:


print (dir(math)) # Prints all functions associated with Math module.


# Help on specific functions in a module
# ----------

# In[15]:


help(math.hypot)


# Strings
# --------
# There are three methods of defining strings.

# In[16]:


a = "John's Computer" # notice the '


# In[17]:


b = 'John said, "This is my computer."' # notice the "


# In[18]:


a_alt = 'John\'s Computer' # now you need the escape sequence \


# In[19]:


b_alt = "John said, \"This is my computer.\"" # again escape sequence.


# In[20]:


long_string = """Hello World! 

I once said to people, "Learn Python!" 

And then they said, "Organize a workshop!" """


# In[21]:


long_string_traditional = 'Hello World! \n\nI once said to people, "Learn Python!" \n\nAnd then they said, "Organize an online course!" '


# * Can be used to dynamically build scripts, both Python-based and other "languages".
# * Used for documenting functions/modules. (To come later!)

# String Arithmetic
# ----------------

# In[22]:


s1 = "Hello" ; s2 = "World!"


# In[23]:


string_sum = s1 + s2
print (string_sum)


# In[24]:


string_product = s1*3
print (string_product)


# In[25]:


print (s1*3+s2)


# String is a sequence!
# ---------------------

# In[26]:


a = "Python rocks!"


# In[27]:


a[0], a[1], a[2] # Positions begin from 0 onwards.


# In[28]:


a[-1], a[-2], a[-3] # Negative indices - count backwards!


# In[29]:


len(a) # Measures length of both sequence/unordered collections!


# Sequences can be sliced!
# ----------

# In[30]:


a[2:6] # elements with indices 2,3,4,5 but not 6


# In[31]:


a[8:-2] # indices 8,9 ... upto 2nd last but not including it.


# In[32]:


a[:5] # Missing first index, 0 assumed.


# In[33]:


a[5:] # Missing last index, len(a) assumed.


# Crazier Slicing
# ---------------

# In[34]:


a[1:6:2],a[1],a[3],a[5] # Indices 1, 3, 5


# In[35]:


a[::2] # beginning to end


# In[36]:


a[::-1] # Reverse slicing!


# In[37]:


a[1:6:-1] # In a[i:j:-1], changes meaning of i and j


# Objects and Methods - An oversimplified Introduction
# =====
# 
# An object can be thought of a construct in the memory.
# 
# It has a well defined behavior with respect to other objects. (2\*3 is allowed, "a"\*"b" is not!)
# 
# The properties of the object, the operations that can be performed all are pre-defined.
# 
# A method is a function bound to an object that can perform specific operations that the object supports.
# 
#     ObjectName.MethodName(arguments)
#     
# OK, let's see some string methods in action!

# String Methods
# --------------

# In[38]:


a = "   I am a string, I am an object, I am immutable!   "


# In[39]:


a.title()


# In[40]:


a.split(",")


# In[41]:


a.strip() # Remove trailing and leading whitespaces.


# Strings are Immutable!
# -----------------------

# In[42]:


print (a) # Check the value!


# In[43]:


a.title() # Transform string to title case ... really?


# In[44]:


print (a) # Nothing changed! Strings are immutabe.


# In[45]:


b = a.title() # String methods return strings instead.


# In[46]:


print (b)


# In[47]:


a[3] = "x" # Immutability implies no in-place changes.


# Getting Help
# -------------

# In[48]:


print (dir(a)) # a is a string object.


# In[49]:


help(a.find)