Python Notes

The following are a few notes on the syntax, commands, etc. for python programming Assumes python version 3.x, which differs substantially from the 2.x versions. Python is not backwards-compatible, so 2.x codes don't necessarily work in 3.x.


python prog.py                   # Run python, where prog.py is a python program
python prog.py arg1 arg2         # Run python program with arguments
                                 #  inside the code you must 'import sys', and then
                                 #    str(sys.argv[0]) is 'prog.py'
                                 #    str(sys.argv[1]) is the first argument, etc.
         
print("It is possible to break long lines of python code",  # breaking long lines
 "by preceding the carriage return with a (, \, or \
 comma")


---------------Numbers, Statistics, Vectors--------------------

# + - / * do what you'd think. ** is a power, abs is absolute value
# pow(2,7) is the same as 2**7, max and min do what you'd think
# round(2.71828,4) gives 2.7183
# 10%3         # 10mod(3) = 1
# str(10%3)    # '1' returned as a string
# float('3.44') # converts a string to a floating point number
                # NOTE: ALL PYTHON inputs from the user are interpreted as strings

# Running it from the command line; most useful as a calculator
python                           # Start python (or python3 for version 3.3)
>>> import numpy as np           # import package numpy
>>> print(np.sin(np.pi/4.))      # prints 0.7071067...; need np.sin as sin is not defined
>>> a=500.3
>>> b=3
>>> a/b                          # prints out 166.7666
>>> _/5.                         # prints 33.3533   _ is the result from the last command
>>> Ctl-D                        # quit

x=[1.3,2.4,8]           # is a list, these behave like strings
x=(1.3,2.4,8)           # is a tuple, these are like lists that can't be changed
x=np.array([1.3,2.4,8]) # is what you want for a vector (1D array)
x.T                     # transpose of x, where x is a numpy array
np.zeros(10, dtype=np.int)  # initialize an integer array of 10 elements to zero
np.ceil([2.87])   # rounds up to 3
np.array([2.87,4.8], dtype=int) # rounds to [2,4]  !!Use this, and not np.int!!
np.floor([2.87,4.8]) # rounds to [2.,4.] but these are reals
np.arange(0,5,0.1) # makes [0, 0.1, 0.2 ... 4.9, 5.0] vector
np.random.rand(5) # five random numbers between 0 and 1
np.random.randn(5) # five random numbers from std normal distribution
np.random.randint(0,99,5) # five random integers between 0 and 99
np.mean(x)       # mean of x
np.var(x)        # variance of x   DONT FORGET THE 'np.' 
np.std(x)        # stddev of x
np.log(x)        # ln(x)
np.log10(x)      # log10(x)
np.exp(x)        # exp(x)
np.arctan(x)     # atan(x)
np.percentile(a,85) # value of the 85th percentile of array a. Very useful for finding 'sigma'
                    # in the presence of outliers! e.g. sigma=np.percentile(a,83.3)-np.percentile,50)

import time
t0=time.time()  #Current time. Place at beginning and end to time a section of code


---------------Plotting--------------------
import matplotlib.pyplot as plt  #MATLAB-like plots
from matplotlib import pyplot as plt # Equivalent syntax for importing
from astropy.io import ascii
data=ascii.read('junk.txt', header_start=0, data_start=1, delimiter=" ")
  #junk.txt has a line of header: name, B-V, and mag, and then data
plt.figure(1)  #in case you want multiple figures or subplots
plt.figure(figsize=(8,15))  #make the plot bigger, change its default shape
plt.subplot(325)  #3 rows, 2 columns of plots; access plot number 5 of these
plt.subplots_adjust(wspace=0.5, hspace=0.5) #extra space between the subplots
plt.subplot2grid((6, 8), (0, 3), rowspan=2, colspan=2)  # More flexible subplots. (6,8) is grid size
                                                        # (0,3) is starting location
plt.plot(data['B-V'],data['mag'], 'bx')  #plot blue x's. r-- is red dashed lines, etc.
plt.plot(x,y,lw=2.2)  #Thicker line width
plt.axis([-0.2,0.8,11,5])  #limits on x- and y-axes
plt.ylim(-2,2) #another way to set y limits
plt.grid(True)  #add a grid
plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
plt.yscale('log')  # log_10 scale on y-axis
plt.ylabel('Mag', fontsize=14)  # y-label
plt.xlabel('B-V', fontsize=14)  # x-label
plt.title(r'$\sigma_{15}$ Some title')  # A plot title. The r means a raw string (for latex \)
plt.hist(x,30,facecolor='b',range=[0.9,1.1]) #histogram of x, 30 bins in [0.9,1.1]
plt.scatter(x,y,c=z,s=sz,vmin=0.8,vmax=1.2) #scatter plot, with colors defined by z, size by sz, over range 0.8-1.2
plt.colorbar()                         #add the colorbar to the scatterplot
plt.show()         # Displays the plot
plt.savefig('output.png') # Save to a file

# colors: (r)ed, (g)reen, (b)lue, (w)hite, blac(k), (c)yan, (m)agenta, (y)ellow,
#  hundreds of others like 'darkblue', 'forestgreen', 'brown', etc.
# linestyles:  : dotted; - solid; -- dashed; -. dot-dashed;  possible to make your own

# Polar plot example plotting (r,theta) and using other columns for point size and labels
fig=plt.figure(figsize=(12,12))
ax=fig.add_subplot(111,projection='polar')   # make a polar plot
plt.rcParams['font.size']=14     # make the default labels bigger
 # An example using sorted data in a file to control point type
 # ax.scatter allows you to use a column to define size and color but not point type. this is a workaround
ax.scatter(np.pi*data['theta'][0:18]/180.,data['r'][0:18],s=data['sz'][0:18],marker='x',color='black')
ax.scatter(np.pi*data['theta'][18:40]/180.,data['r'][18:40],s=data['sz'][18:40],marker='o',color='red')
ax.scatter(np.pi*data['theta'][40:42]/180.,data['r'][40:42],s=data['sz'][40:42],marker='d',color='darkgreen')
ax.scatter(np.pi*data['theta'][42:44]/180.,data['r'][42:44],s=data['sz'][42:44],marker='*',color='black')
ax.set_rmax(3.2)  # radial maximum for a polar plot
ax.set_rticks([0.5,1,1.5,2,2.5,3.0])
ax.set_rlabel_position(313)  # Angle in degrees where the labels run
ax.grid(True)
ax.set_theta_zero_location("S") # Where to put zero degrees on the plot
  # Use a label in a column to identify each point
for iv in range(np.size(data['r'])) :
    plt.text(np.pi*data['ltheta'][iv]/180.,data['lr'][iv]+0.1,data['name'][iv],fontsize=10)
  # Example of a legend
ax.legend(['1-6','6-25','26-50','>50'],fontsize=14,loc='upper right', bbox_to_anchor=(1.15, 1.0))
plt.title('Number of O Stars in Nearby Associations',fontsize=20)
plt.show()



---------------Displaying and Altering Images--------------------
plt.figure(figsize=(20,10))  #set the size
plt.axis('off')       # shuts off drawing of x,y axes 
plt.imshow(imname[20:42,29:48],origin='lower',vmin=-120,  #imname is np.array, vmin and vmax are scale limits
  vmax=20,cmap='jet',interpolation='nearest',aspect='equal') # cmap is colormap, aspect=equal gives square pixels
plt.colorbar()

plt.imshow(blurredz[20:42,29:48],origin='lower',vmin=-120,vmax=20, # OR try using 'extent' to fix the labels
  cmap='jet',interpolation='nearest',       # Fits image [x,y] --> np.array [y,x] via astropy fits.open
  aspect='equal',extent=(29,48,20,42))      # But imshow displays np.array [y,x] as [x,y] again (I think)
                                            # Note, labels x,y flip for extent. 
                                            # If you were to put [2900,4800,2000,4200] for extent it would only
                                            # change the labels

blurredz = gaussian_filter(data3, sigma=1.5, mode='nearest') # smooth an image with a gaussian filter


---------------Strings--------------------

# Assigning, concantating strings
c="hello"
ss=c+" "+c      # ss = "hello hello" 

# uppercase and lowercase
ss= "HEllo"
ss.upper()     #would be HELLO
ss.lower()     #would be hello
ss.isupper()   #returns False because HEllo is not all uppercase

# string length
ss="lasjhf"
len(ss)       # equals 6

# substrings and positions in a string, replacing strings
ss=Hi there"
ss[1:4]      # equals 'i th' because the index starts at 0
ss[len(ss)-3:len(ss)-1]  # 'ere' the last three characters
ss[-2]       # second to last character, 'r'
ss[-2:]      # second to last character to the end of the string, 're'
ss[::-1]     #reverses the string, 'ereht iH'
ss.index("h")    # equals 4 in this case
ss.index("her") # also equals 4, the location of the beginning of 'her' 
ss.replace("her","eak")  # gives 'Hi teake'

#         Formatted print statements
print("{0:5.3f} {1:3d}".format(var,num))  #the 0: is the first argument (var),
                                          #    1: is the second argument (num)
print('Input filename: ',end="")          # stops the carriage return
print('12'.zfill(5))             # for leading zeros, prints 00012


-----I/O into a program, open and write to output files -------

name=input("Input filename: ")                    # read name from terminal
oname="out_"+name               # make output filename
fin= open(name,'r')             # open input filename to read  'a' would be to append
fout= open(oname,'w')           # open output filename to write (overwrite) an entirely new file
for line in fin:                # loop until run out of lines
 line=line.strip()            # get rid of invisible characters like \n
 columns=line.split()         # split the fields of the line
 var1=float(columns[0])      # columns[0] and columns[1] are strings
 var2=float(columns[1])
 fout.write('{0:9.4f} {1:9.4f}\n'.format(var1-var2,var1*var2))
fin.close()
fout.close()

tfile=open('times', "r")
line=tfile.readline()    # read in first line
while line:              # read in subsequent nonzero lines
 print(line)
 line=tfile.readline()
tfile.close()

name.readable()   # True if file can be read
name.readline()   # reads in a line
name.readlines()  # reads file into a list
name.write("\n adding this line to file") # for appending to existing files

a,b = float(input("Type in two values: ").split())  # input 2 real values

from astropy.io import ascii
data=ascii.read('gc.dat', header_start=0, data_start=1, delimiter=" ")
data.more()                      #To interactively page through the table
data.info                        #shows what columns are what and their type
data.colnames                    #list column names
data.rename_column('dia','D')    #rename a column
data['newcol'] = [2,4,5,2,3,5]   #add a column named 'newcol'
data.remove_column('newcol')     #remove a column 
data['dia'].format = '7.3f'      #changes format of column 'dia'
data['dia','B-V']                #a table with only these two columns
data.add_row(3344,344.4,-13.4,12.3,8.9]  #add a row
data1=data[np.where((data['long']<20) & (data['dia']>10))] #extract subset
np.where(a>4)  # returns a 'tuple', with the indices where the condition is true
               # that's why np.where is inside the [ ] in the above subset example
               # most of the time you probably don't want to be doing math on np.where
               # because you'll be doing math on the indices, not on the values
np.where(a>4, a, 5*a) # returns 'a' when a>4, and 5*a otherwise, each in a tuple
np.reshape(a,np.product(a.shape))  # converts a 2D array to a 1D array


------- FITS I/O----------
                       #Reading
from astropy.io import fits
hdul=fits.open('test1.fits') # opens the HDU list
data1=hdul[0].data   # the data
hdr1=hdul[0].header   # the header
hdr1['NAXIS1']  # value of the Fits keyword NAXIS1
ixlim=data1.shape[1]  # Warning!  astropy transposes the array, so x is now y
                      # it will transpose it back again when writing out
iylim=data1.shape[0]

                       #Writing
hduout=fits.PrimaryHDU(dataout.astype(np.float32)) #puts array dataout into a Fits HDU. Default type is 'double' 
hduout.writeto('test2.fits', overwrite=True) # Writes the HDU to an output file


------- if, for, while statements; looping ----------
flag1=True
flag2 = False
if flag1 and flag2:    # you can also use 'or'.  DONT FORGET the :
 print("ok")
elif flag1 and not(flag2): # use of 'not' function
 print("maybe ok")
else:
 print("not ok")
 quit()          #end program if not ok

if num1 >= num2 and num2 != num3:  # For numbers
 print(num1,num2)
print("done with loop")

if ((a[i,j] == 0) | (a[i,j] == 2) & (t==8)) :  # note use of ==, |, &
  print(a[i,j])

for i in range(0,upper) :  # 0,1,2,...upper-1  i.e., the indexed values for an array of length upper
 print(i)

for iv in list(range(8, 12)) :
 print(iv-2.3)

for iv in range(np.size(vs)) :
 print(iv-2)

i=0
while (i< len(data['ra'])): 
 print(i)
 i+=1
print("done with loop")

while True:
 try:
  a=int(input("Input an integer: "))
  break                              # break out of what would be an infinite while loop
 except ValueError:                  # If they didnt input an integer
  a=0
  print("You need an integer here")
print(a)


-----Lists-------
list=["bobby",23,"billy",55.3]   
list[2]         # this is "billy"
list[-1]        # this is 55.3, go from the end
list[2:]        # starts at element 2 and goes to the end:  ["billy",55.3]
list1=["a","b"]
list2=[1,2]
list1.extend(list2) # glues lists together: ["a","b",1,2] and overwrites list1
list2.append(5)     # list2 is now [1,2,5]
list2.index(5)      # returns 2, the index location of element '5'
list2.count(5)      # number of times '5' occurs in the list
list2.insert(1,12)  # list2 is now [1,12,2,5]
list2.remove(12)    # removes first occurrence of 12
list2.pop()         # remove last element from list
list.sort()         # sorts the list
list.reverse()      # reverse sorts the list
list2.clear()       # clears the list
list3=list1.copy()  # copies a list
list4=[[0,3,5],[9,4,2],[0]]  # list4[1][0] = 9 in this case

array = (2,4)       # this is a 'tuple' and can never be changed
                    # so you can't change the 4 to a 5, for example (seems stupid)


---------------Dictionaries--------------------
dateno = {         # this defines the dictionary with a key/value
"jan": "0",        # e.g. mar/59
"feb": "31",
"mar": "59",
"apr": "90",
"may": "120",
"jun": "151",
"jul": "181",
"aug": "212",
"sep": "243",
"oct": "273",
"nov": "304",
"dec": "334",
}
while (True):
 a,b = input("Date (e.g. Feb 22): ").split()  #input multiple values on one line
 aa = dateno.get(a.lower()[0:3],"Bad")        # Apr -> 90, etc. Apq -> "Bad"
 if aa == "Bad":
   print("screwup")
 else:
   print(int(aa)+int(b))



---------------Error Handling--------------------

while True:
 try:
  a=int(input("Input an integer: "))
  break                              # break out of the infinite while loop
 except ValueError:                  # If they didnt input an integer
  print("You need an integer here")
print(a)


---------------Classes --------------------
class exam:              # Define a new class of objects
   def __init__(self,question,answer):   # exam objects consist of a question and an answer
      self.quest = question   # e1.quest will refer to question part of the exam element e1
      self.ans = answer      # e1.ans will refer to answer part of the exam element e1

                # make a list of questions
questions = [
"Is the sky blue?\n(a) yes\n(b) no\n\n",
"Does Trump have yellow hair?\n(a) yes\n(b) no\n\n",
"What color are the Rams jerseys?\n(a) purple\n(b) pink\n(c)yellow and blue\n\n",
]

key = [                  #the list 'key' contains exam objects
exam(questions[0],"a"),   
exam(questions[1],"a"),
exam(questions[2],"c")
]

score=0
for i in key:            #each i is an exam object
 answer=input(i.quest)   #asks for user input with the prompt i.quest = questions[i]
 if(answer == i.ans):
   score += 1
print("You got " + str(score) + "/" + str(len(key)) + " Correct")


---------------Subroutines (functions)--------------------
def yrs_since_HS(age):
  return(age-18)
print(yrs_since_HS(55)) # gives 37.  Input was age, returned value is age-18


---------------Modules and Importing--------------------
import numpy as np
np.pi                 # 3.14159265...
pip install module
pip uninstall module


---------------Interact with the operating system--------------------

# To execute a command from the OS and print the results to STDOUT, precede with a '!'
!pwd

# To call a command from the OS:
from subprocess import call
call(["/bin/ls","-lt"])  # if successful it simply exits with code '0'

# To call a command from the OS that uses pipes:
import subprocess
p1=subprocess.Popen(["/bin/ls"],stdout=subprocess.PIPE)   #first command
p2=subprocess.Popen(["/usr/bin/awk","END{print NR}"],stdin=p1.stdout,stdout=subprocess.PIPE) #pipe to second command
p1.stdout.close() #apparently so the order of finishing doesn't matter
output=p2.communicate()[0] #set the result from the pipe (a byte string) to 'output'
print(output.decode('ascii')) #print out this byte string as ascii

# To write python commands to a file, and then execute that file in a double loop
# Note if you forget the () in g.close the python code will skip one command
#  and you get a real mess, with only 5 output files instead of 6 and mixed up contents
#  That's because the file remains open, and only closes when it reloops
from subprocess import call
import numpy as np
vs=(20,30)
denz=(10,20,30)
for iv in range(np.size(vs)) :
 for id in range(np.size(denz)) :
  fout=open("jj",'w')
  fout.write('{0:10.2f}  {1:4.2f} VS DEN\n'.format(vs[iv],denz[id]))
  fout.close()

  g=open("doit",'w')
  g.write('cp -f jj test_{0:2d}_{1:4.2f}.txt\n'.format(int(vs[iv]),denz[id]))
  g.close()   # Don't forget the ()!!!
  call(["/bin/tcsh","doit"])

---------------Jupyter Notebook--------------------

# To start up Jupyter:
jupyter notebook  # will create a .ipynb file (the notebook) in the current directory
                  # uses  ~/.jupyter/nbconfig/notebook.json for shortcuts
                  # These are vi-like
                  # Jupyter has an insert mode and an escape mode like vi
                  # Don't be afraid to restart the kernel if it gets confused

jupyter nbconvert --to python nbname # Converts nbname.ipynb to nbname.py, a python code file
                                     # you can run via 'python nbname.py'
       # Note, jupyter notebooks are really json files, meaning they
       # are text files that can be edited