Data Science from Scratch: First Principles with Python (2015)
Chapter 2. A Crash Course in Python
People are still crazy about Python after twenty-five years, which I find hard to believe.
Michael Palin
The Basics
Getting Python
pip install ipython
The Zen of Python
There should be one — and preferably only one — obvious way to do it.
Whitespace Formatting
foriin[1,2,3,4,5]:
i# first line in "for i" block
forjin[1,2,3,4,5]:
j# first line in "for j" block
i+j# last line in "for j" block
i# last line in "for i" block
"done looping"
long_winded_computation=(1+2+3+4+5+6+7+8+9+10+11+12+
13+14+15+16+17+18+19+20)
list_of_lists=[[1,2,3],[4,5,6],[7,8,9]]
easier_to_read_list_of_lists=[[1,2,3],
[4,5,6],
[7,8,9]]
two_plus_three=2+\
3
foriin[1,2,3,4,5]:
# notice the blank line
i
IndentationError:expectedanindentedblock
Modules
importre
my_regex=re.compile("[0-9]+",re.I)
importreasregex
my_regex=regex.compile("[0-9]+",regex.I)
importmatplotlib.pyplotasplt
fromcollectionsimportdefaultdict,Counter
lookup=defaultdict(int)
my_counter=Counter()
match=10
fromreimport*# uh oh, re has a match function
match# "<function re.match>"
Arithmetic
from__future__importdivision
Functions
defdouble(x):
"""this is where you put an optional docstring
that explains what the function does.
for example, this function multiplies its input by 2"""
returnx*2
defapply_to_one(f):
"""calls the function f with 1 as its argument"""
returnf(1)
my_double=double# refers to the previously defined function
x=apply_to_one(my_double)# equals 2
y=apply_to_one(lambdax:x+4)# equals 5
another_double=lambdax:2*x# don't do this
defanother_double(x):return2*x# do this instead
defmy_print(message="my default message"):
message
my_print("hello")# prints 'hello'
my_print()# prints 'my default message'
defsubtract(a=0,b=0):
returna-b
subtract(10,5)# returns 5
subtract(0,5)# returns -5
subtract(b=5)# same as previous
Strings
single_quoted_string='data science'
double_quoted_string="data science"
tab_string="\t"# represents the tab character
len(tab_string)# is 1
not_tab_string=r"\t"# represents the characters '\' and 't'
len(not_tab_string)# is 2
multi_line_string="""This is the first line.
and this is the second line
and this is the third line"""
Exceptions
try:
0/0
exceptZeroDivisionError:
"cannot divide by zero"
Lists
integer_list=[1,2,3]
heterogeneous_list=["string",0.1,True]
list_of_lists=[integer_list,heterogeneous_list,[]]
list_length=len(integer_list)# equals 3
listum=sum(integer_list)# equals 6
x=range(10)# is the list [0, 1, ..., 9]
zero=x[0]# equals 0, lists are 0-indexed
one=x[1]# equals 1
nine=x[-1]# equals 9, 'Pythonic' for last element
eight=x[-2]# equals 8, 'Pythonic' for next-to-last element
x[0]=-1# now x is [-1, 1, 2, 3, ..., 9]
first_three=x[:3]# [-1, 1, 2]
three_to_end=x[3:]# [3, 4, ..., 9]
one_to_four=x[1:5]# [1, 2, 3, 4]
last_three=x[-3:]# [7, 8, 9]
without_first_and_last=x[1:-1]# [1, 2, ..., 8]
copy_of_x=x[:]# [-1, 1, 2, ..., 9]
1in[1,2,3]# True
0in[1,2,3]# False
x=[1,2,3]
x.extend([4,5,6])# x is now [1,2,3,4,5,6]
x=[1,2,3]
y=x+[4,5,6]# y is [1, 2, 3, 4, 5, 6]; x is unchanged
x=[1,2,3]
x.append(0)# x is now [1, 2, 3, 0]
y=x[-1]# equals 0
z=len(x)# equals 4
x,y=[1,2]# now x is 1, y is 2
_,y=[1,2]# now y == 2, didn't care about the first element
Tuples
my_list=[1,2]
my_tuple=(1,2)
other_tuple=3,4
my_list[1]=3# my_list is now [1, 3]
try:
my_tuple[1]=3
exceptTypeError:
"cannot modify a tuple"
defsum_and_product(x,y):
return(x+y),(x*y)
sp=sum_and_product(2,3)# equals (5, 6)
s,p=sum_and_product(5,10)# s is 15, p is 50
x,y=1,2# now x is 1, y is 2
x,y=y,x# Pythonic way to swap variables; now x is 2, y is 1
Dictionaries
empty_dict={}# Pythonic
empty_dict2=dict()# less Pythonic
grades={"Joel":80,"Tim":95}# dictionary literal
joels_grade=grades["Joel"]# equals 80
try:
kates_grade=grades["Kate"]
exceptKeyError:
"no grade for Kate!"
joel_has_grade="Joel"ingrades# True
kate_has_grade="Kate"ingrades# False
joels_grade=grades.get("Joel",0)# equals 80
kates_grade=grades.get("Kate",0)# equals 0
no_ones_grade=grades.get("No One")# default default is None
grades["Tim"]=99# replaces the old value
grades["Kate"]=100# adds a third entry
num_students=len(grades)# equals 3
tweet={
"user":"joelgrus",
"text":"Data Science is Awesome",
"retweet_count":100,
"hashtags":["#data","#science","#datascience","#awesome","#yolo"]
}
tweet_keys=tweet.keys()# list of keys
tweet_values=tweet.values()# list of values
tweet_items=tweet.items()# list of (key, value) tuples
"user"intweet_keys# True, but uses a slow list in
"user"intweet# more Pythonic, uses faster dict in
"joelgrus"intweet_values# True
defaultdict
word_counts={}
forwordindocument:
ifwordinword_counts:
word_counts[word]+=1
else:
word_counts[word]=1
word_counts={}
forwordindocument:
try:
word_counts[word]+=1
exceptKeyError:
word_counts[word]=1
word_counts={}
forwordindocument:
previous_count=word_counts.get(word,0)
word_counts[word]=previous_count+1
fromcollectionsimportdefaultdict
word_counts=defaultdict(int)# int() produces 0
forwordindocument:
word_counts[word]+=1
dd_list=defaultdict(list)# list() produces an empty list
dd_list[2].append(1)# now dd_list contains {2: [1]}
dd_dict=defaultdict(dict)# dict() produces an empty dict
dd_dict["Joel"]["City"]="Seattle"# { "Joel" : { "City" : Seattle"}}
dd_pair=defaultdict(lambda:[0,0])
dd_pair[2][1]=1# now dd_pair contains {2: [0,1]}
fromcollectionsimportCounter
c=Counter([0,1,2,0])# c is (basically) { 0 : 2, 1 : 1, 2 : 1 }
word_counts=Counter(document)
# print the 10 most common words and their counts
forword,countinword_counts.most_common(10):
word,count
s=set()
s.add(1)# s is now { 1 }
s.add(2)# s is now { 1, 2 }
s.add(2)# s is still { 1, 2 }
x=len(s)# equals 2
y=2ins# equals True
z=3ins# equals False
stopwords_list=["a","an","at"]+hundreds_of_other_words+["yet","you"]
"zip"instopwords_list# False, but have to check every element
stopwords_set=set(stopwords_list)
"zip"instopwords_set# very fast to check
item_list=[1,2,3,1,2,3]
num_items=len(item_list)# 6
item_set=set(item_list)# {1, 2, 3}
num_distinct_items=len(item_set)# 3
distinct_item_list=list(item_set)# [1, 2, 3]
Control Flow
if1>2:
message="if only 1 were greater than two..."
elif1>3:
message="elif stands for 'else if'"
else:
message="when all else fails use else (if you want to)"
parity="even"ifx%2==0else"odd"
x=0
whilex<10:
x,"is less than 10"
x+=1
forxinrange(10):
x,"is less than 10"
forxinrange(10):
ifx==3:
continue# go immediately to the next iteration
ifx==5:
break# quit the loop entirely
x
Truthiness
one_is_less_than_two=1<2# equals True
true_equals_false=True==False# equals False
x=None
x==None# prints True, but is not Pythonic
xisNone# prints True, and is Pythonic
§ False
§ None
§ [] (an empty list)
§ {} (an empty dict)
§ ""
§ set()
§ 0
§ 0.0
ifs:
first_char=s[0]
else:
first_char=""
first_char=sands[0]
safe_x=xor0
all([True,1,{3}])# True
all([True,1,{}])# False, {} is falsy
any([True,1,{}])# True, True is truthy
all([])# True, no falsy elements in the list
any([])# False, no truthy elements in the list
x=[4,1,2,3]
y=sorted(x)# is [1,2,3,4], x is unchanged
x.sort()# now x is [1,2,3,4]
# sort the list by absolute value from largest to smallest
x=sorted([-4,1,-2,3],key=abs,reverse=True)# is [-4,3,-2,1]
# sort the words and counts from highest count to lowest
wc=sorted(word_counts.items(),
key=lambda(word,count):count,
reverse=True)
List Comprehensions
even_numbers=[xforxinrange(5)ifx%2==0]# [0, 2, 4]
squares=[x*xforxinrange(5)]# [0, 1, 4, 9, 16]
even_squares=[x*xforxineven_numbers]# [0, 4, 16]
square_dict={x:x*xforxinrange(5)}# { 0:0, 1:1, 2:4, 3:9, 4:16 }
square_set={x*xforxin[1,-1]}# { 1 }
zeroes=[0for_ineven_numbers]# has the same length as even_numbers
pairs=[(x,y)
forxinrange(10)
foryinrange(10)]# 100 pairs (0,0) (0,1) ... (9,8), (9,9)
increasing_pairs=[(x,y)# only pairs with x < y,
forxinrange(10)# range(lo, hi) equals
foryinrange(x+1,10)]# [lo, lo + 1, ..., hi - 1]
Generators and Iterators
deflazy_range(n):
"""a lazy version of range"""
i=0
whilei<n:
yieldi
i+=1
foriinlazy_range(10):
do_something_with(i)
defnatural_numbers():
"""returns 1, 2, 3, ..."""
n=1
whileTrue:
yieldn
n+=1
TIP
lazy_evens_below_20=(iforiinlazy_range(20)ifi%2==0)
Randomness
importrandom
four_uniform_randoms=[random.random()for_inrange(4)]
# [0.8444218515250481, # random.random() produces numbers
# 0.7579544029403025, # uniformly between 0 and 1
# 0.420571580830845, # it's the random function we'll use
# 0.25891675029296335] # most often
random.seed(10)# set the seed to 10
random.random()# 0.57140259469
random.seed(10)# reset the seed to 10
random.random()# 0.57140259469 again
random.randrange(10)# choose randomly from range(10) = [0, 1, ..., 9]
random.randrange(3,6)# choose randomly from range(3, 6) = [3, 4, 5]
up_to_ten=range(10)
random.shuffle(up_to_ten)
up_to_ten
# [2, 5, 1, 9, 7, 3, 8, 6, 4, 0] (your results will probably be different)
my_best_friend=random.choice(["Alice","Bob","Charlie"])# "Bob" for me
lottery_numbers=range(60)
winning_numbers=random.sample(lottery_numbers,6)# [16, 36, 10, 6, 25, 9]
four_with_replacement=[random.choice(range(10))
for_inrange(4)]
# [9, 4, 4, 2]
Regular Expressions
importre
all([# all of these are true, because
notre.match("a","cat"),# * 'cat' doesn't start with 'a'
re.search("a","cat"),# * 'cat' has an 'a' in it
notre.search("c","dog"),# * 'dog' doesn't have a 'c' in it
3==len(re.split("[ab]","carbs")),# * split on a or b to ['c','r','s']
"R-D-"==re.sub("[0-9]","-","R2D2")# * replace digits with dashes
])# prints True
Object-Oriented Programming
# by convention, we give classes PascalCase names
classSet:
# these are the member functions
# every one takes a first parameter "self" (another convention)
# that refers to the particular Set object being used
def__init__(self,values=None):
"""This is the constructor.
It gets called when you create a new Set.
You would use it like
s1 = Set() # empty set
s2 = Set([1,2,2,3]) # initialize with values"""
self.dict={}# each instance of Set has its own dict property
# which is what we'll use to track memberships
ifvaluesisnotNone:
forvalueinvalues:
self.add(value)
def__repr__(self):
"""this is the string representation of a Set object
if you type it at the Python prompt or pass it to str()"""
return"Set: "+str(self.dict.keys())
# we'll represent membership by being a key in self.dict with value True
defadd(self,value):
self.dict[value]=True
# value is in the Set if it's a key in the dictionary
defcontains(self,value):
returnvalueinself.dict
defremove(self,value):
delself.dict[value]
s=Set([1,2,3])
s.add(4)
s.contains(4)# True
s.remove(3)
s.contains(3)# False
Functional Tools
defexp(base,power):
returnbase**power
deftwo_to_the(power):
returnexp(2,power)
fromfunctoolsimportpartial
two_to_the=partial(exp,2)# is now a function of one variable
two_to_the(3)# 8
square_of=partial(exp,power=2)
square_of(3)# 9
defdouble(x):
return2*x
xs=[1,2,3,4]
twice_xs=[double(x)forxinxs]# [2, 4, 6, 8]
twice_xs=map(double,xs)# same as above
list_doubler=partial(map,double)# *function* that doubles a list
twice_xs=list_doubler(xs)# again [2, 4, 6, 8]
defmultiply(x,y):returnx*y
products=map(multiply,[1,2],[4,5])# [1 * 4, 2 * 5] = [4, 10]
defis_even(x):
"""True if x is even, False if x is odd"""
returnx%2==0
x_evens=[xforxinxsifis_even(x)]# [2, 4]
x_evens=filter(is_even,xs)# same as above
listvener=partial(filter,is_even)# *function* that filters a list
x_evens=listvener(xs)# again [2, 4]
x_product=reduce(multiply,xs)# = 1 * 2 * 3 * 4 = 24
list_product=partial(reduce,multiply)# *function* that reduces a list
x_product=list_product(xs)# again = 24
enumerate
# not Pythonic
foriinrange(len(documents)):
document=documents[i]
do_something(i,document)
# also not Pythonic
i=0
fordocumentindocuments:
do_something(i,document)
i+=1
fori,documentinenumerate(documents):
do_something(i,document)
foriinrange(len(documents)):do_something(i)# not Pythonic
fori,_inenumerate(documents):do_something(i)# Pythonic
zip and Argument Unpacking
list1=['a','b','c']
list2=[1,2,3]
zip(list1,list2)# is [('a', 1), ('b', 2), ('c', 3)]
pairs=[('a',1),('b',2),('c',3)]
letters,numbers=zip(*pairs)
zip(('a',1),('b',2),('c',3))
defadd(a,b):returna+b
add(1,2)# returns 3
add([1,2])# TypeError!
add(*[1,2])# returns 3
args and kwargs
defdoubler(f):
defg(x):
return2*f(x)
returng
deff1(x):
returnx+1
g=doubler(f1)
g(3)# 8 (== ( 3 + 1) * 2)
g(-1)# 0 (== (-1 + 1) * 2)
deff2(x,y):
returnx+y
g=doubler(f2)
g(1,2)# TypeError: g() takes exactly 1 argument (2 given)
defmagic(*args,**kwargs):
"unnamed args:",args
"keyword args:",kwargs
magic(1,2,key="word",key2="word2")
# prints
# unnamed args: (1, 2)
# keyword args: {'key2': 'word2', 'key': 'word'}
defother_way_magic(x,y,z):
returnx+y+z
x_y_list=[1,2]
z_dict={"z":3}
other_way_magic(*x_y_list,**z_dict)# 6
defdoubler_correct(f):
"""works no matter what kind of inputs f expects"""
defg(*args,**kwargs):
"""whatever arguments g is supplied, pass them through to f"""
return2*f(*args,**kwargs)
returng
g=doubler_correct(f2)
g(1,2)# 6
Welcome to DataSciencester!
§ There is no shortage of Python tutorials in the world. The official one is not a bad place to start.
§ The official IPython tutorial is not quite as good. You might be better off with their videos and presentations. Alternatively, Wes McKinney’s Python for Data Analysis (O’Reilly) has a really good IPython chapter.
All materials on the site are licensed Creative Commons Attribution-Sharealike 3.0 Unported CC BY-SA 3.0 & GNU Free Documentation License (GFDL)
If you are the copyright holder of any material contained on our site and intend to remove it, please contact our site administrator for approval.
© 2016-2026 All site design rights belong to S.Y.A.