In :
from IPython.display import YouTubeVideo

Out: UC Berkeley Python Bootcamp (c) J. Bloom All Rights Reserved

## There are four main types of collections of data ("Sequence objects")¶

•   Lists: a mutable array of data
•   Tuples: ordered, immutable list
•   Sets: unordered collection of unique elements
•   Dictionaries: keyword/value lookup



The value in each element can be whatever (type) you want.

string is actually a sequence object

### Tuple¶

denoted with parentheses

In [ ]:
t = (12,-1)
print(type(t))

In [ ]:
print(isinstance(t,tuple))
print(len(t))

In [ ]:
t = (12,"monty",True,-1.23e6)
print(t)

In [ ]:
print(t[-1])

In [ ]:
t[-2:]  # get the last two elements, return as a tuple

In [ ]:
x = (True) ; print(type(x))
x = (True,) ; print(type(x))

In [ ]:
type(()), len(())

In [ ]:
type((,))


single-element tuples look like (element,)

cannot change a tuple but you can create new one with concatenation

In [ ]:
t = False

In [ ]:
type((1,"spam","eggs", None))

In [ ]:
t[0:2], False, t[3:]

In [ ]:
## the above it
## not what we wanted... need to concatenate
t[0:2] + False + t[3:]

In [ ]:
y = t[0:2] + (False,) + t[3:] ; print(y)

In [ ]:
t*2


### List¶

#### denoted with a brackets¶

In [ ]:
v = [1,2,3] ; print(len(v), type(v))

In [ ]:
v[0:2]

In [ ]:
v = ["eggs","spam",-1,("monty","python"),[-1.2,-3.5]]
len(v)

In [ ]:
v ="green egg"
v += ",love it."
v[-1]

In [ ]:
v[-1]

In [ ]:
v[-1] = None ; print(v)

In [ ]:
v = v[2:] ; print(v)

In [ ]:
# let's make a proto-array out of nested lists
vv = [ [1,2], [3,4] ]

In [ ]:
print(len(vv))

In [ ]:
determinant = vv*vv - vv*vv
print(determinant)


the main point here: lists are changeable ("mutable")

### lists can be extended & appended¶

In [ ]:
v = [1,2,3]
v.append(4)
print(v)


Lists can be considered objects. Objects are like animals: they know how to do stuff (like eat and sleep), they know how to interact with others (like make children), and they have characteristics (like height, weight).

"Knowing how to do stuff" with itself is called a method. In this case "append" is a method which, when invoked, is an action that changes the characteristics (the data vector of the list itself).

### List¶

#### lists can be extended, appended, and popped¶

In [ ]:
v = [1,2,3]
v.append(4)
v.append([-5]) ; print(v)

In [ ]:
v = v[:4]
w = ['elderberries', 'eggs']
v + w

In [ ]:
v.extend(w) ; print(v)

In [ ]:
v.pop()

In [ ]:
print(v)

In [ ]:
v.pop(0) ; print(v) ## pop the first element

In [ ]:
v.append(0)

In [ ]:
v = ["hello"] + v

In [ ]:
print(v)

• .append(): adds a new element
• .extend(): concatenates a list/element
• .pop(): remove an element

#### lists can be searched, sorted, & counted¶

In [ ]:
v = [1,3, 2, 3, 4, 1.3]
v.sort() ; print(v)


If there isn't a natural way to compare elements, the sort will fail.

reverse is a keyword of the .sort() method

In [ ]:
import math
v = [1,3, 2, 3, 4, math.pi]
v.sort() ; print(v)

In [ ]:
v.sort(reverse=True) ; print(v)

In [ ]:
v.sort?


.sort() changes the the list in place

In [ ]:
v.index(4)   ## lookup the index of the entry 4

In [ ]:
v.index(3)

In [ ]:
v.count(3)

In [ ]:
v.insert(0,"it's full of stars") ; print(v)

In [ ]:
v.remove(3.0) ; print(v)


### IPython is your new best friend¶

1. Type v. then the Tab button

2. Type v.re then the Tab button

3. Type v.remove?

In [ ]:
## try it here

In [ ]:



### List¶

#### iteration¶

In [ ]:
x = 1
a = ['cat', 'window', 'defenestrate']
for x in a:
print(x, len(x))
if x == "cat":
a.append("?")
print(x)

In [ ]:
for i,x in enumerate(a):
print(i, x, len(x))

In [ ]:
b = enumerate(a)

In [ ]:
print(b.__next__())

In [ ]:
for x in a:
print(x, end=',')


The syntax for iteration is...

for variable_name in iterable:
# do something with variable_name

The range() function

In [ ]:
x = list(range(4)) ; print(x)
total = 0
for val in range(4):
total += val
print("By adding " + str(val) + \
" the total is now " + str(total))


range([start,] stop[, step]) → list of integers

In [ ]:
total = 0
for x in range(1,10,2):
total += x
print("By adding " + str(x) + \
" the total is now " + str(total))

In [ ]:
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
print(i, a[i])


### Sets¶

#### denoted with a curly braces¶

In [ ]:
{1,2,3,"bingo"}

In [ ]:
print(type({1,2,3,"bingo"}))

In [ ]:
print(type({}))

In [ ]:
print(type(set()))

In [ ]:
set(["spamIam"])


sets have unique elements. They can be compared, differenced, unionized, etc.

In [ ]:
a = set("sp") ; b = set("am"); print(a) ; print(b)

In [ ]:
c = set(["m","a"])
c == b

In [ ]:
"p" in a

In [ ]:
"ps" in a

In [ ]:
q = set("spamIam")
a.issubset(q)

In [ ]:
a | b

In [ ]:
q - (a | b)

In [ ]:
q & (a | b)


Like lists, we can use as (unordered) buckets .pop() gives us a random element

In [ ]:
# this is pretty volitile...wont be the same
# order on all machines
for i in q & (a | b):
print(i, end=' ')

In [ ]:
q.remove("a")

In [ ]:
q.pop()

In [ ]:
print(q.pop())
print(q.pop())

In [ ]:
print(q.pop())

In [ ]:
print(q)

In [ ]:
q.pop()


## Dictionaries¶

denoted with a curly braces and colons

In [ ]:
d = {"favorite cat": None, "favorite spam": "all"}


these are key: value, key: value, ...

In [ ]:
print(d["favorite cat"])
d   ## this is not a list and you dont have a keyword = 0

In [ ]:
e = {"favorite cat": None, "favorite spam": "all", \
1: 'loneliest number'}
e == 'loneliest number'


dictionaries are UNORDERED*.

You cannot assume that one key comes before or after another

* you can use a special type of ordered dict if you really need it:

https://docs.python.org/3.1/whatsnew/3.1.html#pep-372-ordered-dictionaries

### 4 ways to make a Dictionary¶

In [ ]:
# number 1...you've seen this
d = {"favorite cat": None, "favorite spam": "all"}

In [ ]:
# number 2
d = dict(one = 1, two=2,cat = 'dog') ; print(d)

In [ ]:
# number 3 ... just start filling in items/keys
d = {}  # empty dictionary
d['cat'] = 'dog'
d['one'] = 1
d['two'] = 2
d

In [ ]:
# number 4... start with a list of tuples
mylist = [("cat","dog"), ("one",1),("two",2)]
print(dict(mylist))

In [ ]:
dict(mylist) == d

In [ ]:
list(d)


#### Dictionaries: they can be complicated (in a good way)¶

In [ ]:
d = {"favorite cat": None, "favorite spam": "all"}

In [ ]:
d = {'favorites': {'cat': None, 'spam': 'all'}, \
'least favorite': {'cat': 'all', 'spam': None}}
print(d['least favorite']['cat'])


remember: the backslash () allows you to across break lines. Not technically needed when defining a dictionary or list

In [ ]:
phone_numbers = {'family': [('mom','642-2322'),('dad','534-2311')],\
'friends': [('Billy','652-2212')]}

In [ ]:
for group_type in ['friends','family']:
print("Group",group_type,":",sep=" ")
for info in phone_numbers[group_type]:
print("\t",info,info,sep=" ")

In [ ]:
# this will return a list, but you dont know in what order!
list(phone_numbers.keys())

In [ ]:
list(phone_numbers.values())[-1]


.keys() and .values(): are called methods on dictionaries

In [ ]:
for group_type in list(phone_numbers.keys()):
print("Group",group_type,":",sep=" ")
for info in phone_numbers[group_type]:
print("\t",info,info,sep=" ")


we cannot ensure ordering here of the groups

In [ ]:
groups = list(phone_numbers.keys())
groups.sort()
for group_type in groups:
print("Group",group_type,":",sep=" ")
for info in phone_numbers[group_type]:
print("\t",info,info,sep=" ")


.items() is a handy method, returning key,value pairs with each iteration

In [ ]:
for group_type, vals in phone_numbers.items():
print("Group",group_type,":",sep=" ")
for info in vals:
print("\t",info,info,sep=" ")


Some examples of getting values:

In [ ]:
phone_numbers['co-workers']

In [ ]:
'co-workers' in phone_numbers

In [ ]:
print(phone_numbers.get('co-workers'))

In [ ]:
phone_numbers.get('friends') == phone_numbers['friends']


Defaults in a get

In [ ]:
print(phone_numbers.get('co-workers',"all alone"))

In [ ]:
phone_numbers.get?


#### setting values¶

you can edit the values of keys and also .pop() & del to remove certain keys

In [ ]:
"BFFs" in phone_numbers.keys()

In [ ]:
# add to the friends list
phone_numbers['friends'].append(("Marsha","232-1121"))
print(phone_numbers)

In [ ]:
## billy's number changed
phone_numbers['friends'] = "532-1521"

In [ ]:
phone_numbers['friends'] = ("Billy","532-1521")

In [ ]:
## I lost all my friends preparing for this Python class
phone_numbers['friends'] = [] # sets this to an empty list

In [ ]:
## remove the friends key altogether
print(phone_numbers.pop('friends'))

In [ ]:
print(phone_numbers)

In [ ]:
del phone_numbers['family']

In [ ]:
print(phone_numbers)


.update() method is very handy, like .append() for lists

In [ ]:
phone_numbers.update({"friends": [("Billy's Brother, Bob", "532-1521")]})
print(phone_numbers)


### Casting Back and Forth¶

In [ ]:
a = [1,2,3,("b",1),("b",1)]

In [ ]:
b = tuple(a) ; print(b)

In [ ]:
print(list(b))

In [ ]:
set(a)

In [ ]:
list(set("spam"))


casting only affects top-level structure, not the elements

## List Comprehension¶

You can create lists "on the fly" by asking simple questions of other iterateable data structures

example: I want a list of all numbers from 0 - 100 whose lowest two bits are both one (e.g., 3, 7, ...) but is not divisible by 11

In [ ]:
mylist = []
for num in range(101):
if (num & 2) and (num & 1) and (num % 11 != 0.0):
mylist.append(num)
print(mylist)

In [ ]:
mylist=[num for num in range(101) if (num & 2) \
and (num & 1) and (num % 11 != 0.0)]
print(mylist)


example: I want a list of all mesons whose masses are between 100 and 1000 MeV

In [ ]:
particles = \
[{"name":"π+"  ,"mass": 139.57018}, {"name":"π0"  ,"mass": 134.9766},
{"name":"η5"  ,"mass": 47.853}, {"name":"η′(958)","mass": 957.78},
{"name":"ηc(1S)", "mass": 2980.5}, {"name": "ηb(1S)","mass": 9388.9},
{"name":"K+",  "mass": 493.677}, {"name":"K0"  ,"mass": 497.614},
{"name":"K0S" ,"mass":  497.614}, {"name":"K0L" ,"mass":  497.614},
{"name":"D+"  ,"mass": 1869.62}, {"name":"D0"  ,"mass": 1864.84},
{"name":"D+s" ,"mass":  1968.49}, {"name":"B+"  ,"mass": 5279.15},
{"name":"B0"  ,"mass": 5279.5}, {"name":"B0s" ,"mass":  5366.3},
{"name":"B+c" ,"mass":    6277}]

# data source: http://en.wikipedia.org/wiki/List_of_mesons

my_mesons = [ (x['name'],x['mass']) for \
x in particles if x['mass'] <= 1000.0 and x['mass'] >= 100.0]

In [ ]:
# get the average
tot = 0.0
for x in my_mesons: tot += x
print("The average meson mass in this range is " + str(tot/len(my_mesons)) \
+ " MeV/c^2.")

In [ ]:
print("The average meson mass in this range is " + str(round(tot/len(my_mesons),2)) \
+ " MeV/c^2.")

In [ ]:
my_mesons

In [ ]:
bytes(my_mesons,encoding="utf-8")


## Breakout Session Work¶

Consider the following data (file: airline.py):

In [ ]:
# %load airline.py
airports = {"DCA": "Washington, D.C.", "IAD": "Dulles", "LHR": "London-Heathrow", \
"SVO": "Moscow", "CDA": "Chicago-Midway", "SBA": "Santa Barbara", "LAX": "Los Angeles",\
"JFK": "New York City", "MIA": "Miami", "AUM": "Austin, Minnesota"}

# airline, number, heading to, gate, time (decimal hours)
("Aeroflot",34,"SVO",5,9.00),("Southwest",146,"CDA",1,9.60), ("United",46,"LAX",5,6.5),\
("Southwest",23,"SBA",6,12.5),("United",2,"LAX",10,12.5),("Southwest",59,"LAX",11,14.5),\
("American", 1,"JFK",12,11.3),("USAirways", 8,"MIA",20,13.1),("United",2032,"MIA",21,15.1),\
("SpamAir",1,"AUM",42,14.4)]


### 1. print out a schedule organized by airline:¶

Flight          Destination          Gate   Time
--------------------------------------------------
Aeroflot 34    Moscow               5   9.0
American 1     New York City        12  11.3
Southwest 23   Santa Barbara        6   12.5
Southwest 59   Los Angeles          11  14.5
...


### 2. print out a schedule organized by time¶

hint: you'll need to do a manual sorting on the last element of each flight element, before beginning the printing loop In [ ]: