my_list = [1, 2, 3, [1, 2, 3, [1, 2, 3]]]
my_list[1, 2, 3, [1, 2, 3, [1, 2, 3]]]
FINA 6333 for Spring 2024
A list is a collection of items that are ordered and changeable. You can add, remove, or modify elements in a list. In Python, you can create an empty list using either [] or list().
my_list = [1, 2, 3, [1, 2, 3, [1, 2, 3]]]
my_list[1, 2, 3, [1, 2, 3, [1, 2, 3]]]
Python is zero-indexed!
my_list[1]2
We can chain indexes!
my_list[-1][-1][-1]3
my_list[-1][-1][-1] = 2_001
my_list[1, 2, 3, [1, 2, 3, [1, 2, 2001]]]
A tuple is similar to a list, but it is immutable, meaning that you cannot change its contents once it has been created. You can create a tuple using parentheses () or the tuple() function.
my_tuple = (1, 2, 3, (1, 2, 3, (1, 2, 3)))
my_tuple(1, 2, 3, (1, 2, 3, (1, 2, 3)))
my_tuple[-1][-1][-1]3
Tuples are immutable, so they cannot be changed!
# my_tuple[-1][-1][-1] = 2_001
# ---------------------------------------------------------------------------
# TypeError Traceback (most recent call last)
# Cell In[12], line 1
# ----> 1 my_tuple[-1][-1][-1] = 2_001
# TypeError: 'tuple' object does not support item assignmentA dictionary is a collection of key-value pairs that are unordered and changeable. You can add, remove, or modify elements in a dictionary. In Python, you can create an empty dictionary using either {} or the dict() function.
my_dict = {'A': 1, 'B': 2, 3: 2_001, 'D': [1, 2, 3]}
my_dict{'A': 1, 'B': 2, 3: 2001, 'D': [1, 2, 3]}
my_dict['A']1
my_dict['E'] = [4, 5, 6]
my_dict{'A': 1, 'B': 2, 3: 2001, 'D': [1, 2, 3], 'E': [4, 5, 6]}
A list comprehension is a concise way of creating a new list by iterating over an existing list or other iterable object. It is more time and space-efficient than traditional for loops and offers a cleaner syntax. The basic syntax of a list comprehension is new_list = [expression for item in iterable if condition] where:
expression is the operation to be performed on each element of the iterableitem is the current element being processediterable is the list or other iterable object being iterated overcondition is an optional filter that only accepts items that evaluate to True.For example, we can use the following list comprehension to create a new list of even numbers from 0 to 8: even_numbers = [x for x in range(9) if x % 2 == 0]
List comprehensions are a powerful tool in Python that can help you write more efficient and readable code (i.e., more Pythonic code).
What if we wanted multiples of 3 from 1 to 25?
[i for i in range(1, 26) if i%3 == 0][3, 6, 9, 12, 15, 18, 21, 24]
The list comprehension above is a simplification of:
empty_list = []
for i in range(1, 26):
if i%3 == 0:
empty_list.append(i)
empty_list[3, 6, 9, 12, 15, 18, 21, 24]
a and b using a third variable c.a = 1b = 2c = aa = bb = cdel cprint(f'Variable a is {a}, and variable b is {b}')Variable a is 2, and variable b is 1
a and b without using a third variable c.a = 1b = 2b, a = a, bprint(f'Variable a is {a}, and variable b is {b}')Variable a is 2, and variable b is 1
The parentheses () around tuples are optional, but often very helpful! The commas , make the tuple, not the parentheses.
a = 1b = 2(b, a) = (a, b)print(f'Variable a is {a}, and variable b is {b}')Variable a is 2, and variable b is 1
1, 1, 1 == (1, 1, 1)(1, 1, False)
Without parentheses (), Python reads the final element in the tuple as 1 == (1, 1, 1), which is False. We can use parentheses () to force Python to do what we want!
(1, 1, 1) == (1, 1, 1)True
For this example, we must use parentheses () to be unambiguos!
(1, 1, 1) == 1, 1, 1(False, 1, 1)
l1 of integers from 1 to 100.l1 = list(range(1, 101))
# the last entry in a code cell automatically prints
# here we use print() to wrap the list to take up less space
print(l1) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
l1 to create a list of integers from 60 to 50 (inclusive).Name this list l2.
l1.index(50)49
l1[49:60][50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
In the solution above, we have to “brute force” picking the left edge at 49. However these are 11 values in 50 to 60 inclusive (i.e., 60 - 50 + 1), so we can add 11 to 49 to find the right edge. This trick is one of the benefits of Python’s zero-indexing.
How do we reverse? I prefer [::-1].
l2 = l1[49:60][::-1] # the first [] slices, and the second [] reverses
l2[60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50]
l2_alt = l1[49:60]
l2_alt.reverse() # note, most core python operators modify the object "in place"
l2_alt[60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50]
l3 of odd integers from 1 to 21.l3 = l1[0:21][::2]
l3[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
l1[0:21:2][1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
list(range(1, 22, 2))[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
[i for i in range(22) if i%2 != 0][1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
What is the advantage of the list comprehension? There is no advantage in this example, but the list comprehension lets us specify several conditions and variable step sizes.
l4 of the squares of integers from 1 to 100.l4 = [i**2 for i in range(1, 101)]
print(l4)[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801, 10000]
l5 that contains the squares of odd integers from 1 to 100.l5 = [i**2 for i in range(1, 101) if i%2 != 0]
print(l5)[1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625, 729, 841, 961, 1089, 1225, 1369, 1521, 1681, 1849, 2025, 2209, 2401, 2601, 2809, 3025, 3249, 3481, 3721, 3969, 4225, 4489, 4761, 5041, 5329, 5625, 5929, 6241, 6561, 6889, 7225, 7569, 7921, 8281, 8649, 9025, 9409, 9801]
strings by the last letter.strings = ['card', 'aaaa', 'foo', 'bar', 'abab']Most core Python methods modify their objects in place. For clarity, I will use the sorted() function, which does not modify its argument in place.
sorted(strings)['aaaa', 'abab', 'bar', 'card', 'foo']
We can pass a function name to the key= argument. Python applis this function to every element in the list, then uses the function output to sort the list.
sorted(strings, key=len)['foo', 'bar', 'card', 'aaaa', 'abab']
sorted(strings, key=len, reverse=True)['card', 'aaaa', 'abab', 'foo', 'bar']
We can index and slice strings just like we do lists!
'string'[-1]'g'
'string'[-2:]'ng'
def get_last_letter(x):
return x[-1]get_last_letter('string')'g'
sorted(strings, key=get_last_letter)['aaaa', 'abab', 'card', 'foo', 'bar']
Anonymous functions (also called lambda functions) let us skip the function writing and naming steps!
sorted(strings, key=lambda x: x[-1])['aaaa', 'abab', 'card', 'foo', 'bar']
nums and an integer k, return the \(k^{th}\) largest element in the array.Note that it is the \(k^{th}\) largest element in the sorted order, not the \(k^{th}\) distinct element.
Example 1:
Input: nums = [3,2,1,5,6,4], k = 2
Output: 5
Example 2:
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4
I saw this question on LeetCode.
def get_largest(x, k):
return sorted(x)[-k]get_largest(x=[3,2,1,5,6,4], k=2)5
get_largest(x=[3,2,3,1,2,4,5,5,6], k=4)4
nums and an integer k, return the k most frequent elements.You may return the answer in any order.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = [1], k = 1
Output: [1]
I saw this question on LeetCode.
def get_frequent(nums, k):
counts = {}
for n in nums:
if n in counts:
counts[n] += 1
else:
counts[n] = 1
return [x[0] for x in sorted(counts.items(), key=lambda x: x[1], reverse=True)[:k]]get_frequent(nums=[1,1,1,2,2,3], k=2)[1, 2]
Input: ["aba", "no"]
Output: [True, False]
We can reverse a string with a [::-1] slice just like we reverse a string!
def is_palindrome(xs):
return [x == x[::-1] for x in xs]is_palindrome(["aba", "no"])[True, False]
returns() that accepts lists of prices and dividends and returns a list of returns.prices = [100, 150, 100, 50, 100, 150, 100, 150]
dividends = [1, 1, 1, 1, 2, 2, 2, 2]Although loop counters are un-Pythonic, this calculation is the rare case where I found loop counters more clear.
def returns(p, d):
rs = []
for i in range(1, len(p)):
r = (p[i] + d[i] - p[i-1]) / p[i-1]
rs.append(r)
return rsreturns(p=prices, d=dividends)[0.51, -0.32666666666666666, -0.49, 1.04, 0.52, -0.32, 0.52]
Here is a solution that avoids a loop counter and combines a list comprehension zip() to loop over prices, dividends, and lagged prices. This solution may be a little more Pythonic, but I find it harder to follow that our loop-counter solution above.
[(p + d - p_lag) / p_lag for p, d, p_lag in zip(prices[1:], dividends[1:], prices[:-1])][0.51, -0.32666666666666666, -0.49, 1.04, 0.52, -0.32, 0.52]
returns() so it returns lists of returns, capital gains yields, and dividend yields.def returns_2(p, d):
rs, d_ps, cgs = [], [], []
for i in range(1, len(p)):
r = (p[i] + d[i] - p[i-1]) / p[i-1]
d_p = d[i] / p[i-1]
cg = r - d_p
rs.append(r)
d_ps.append(d_p)
cgs.append(cg)
return {'r':rs, 'd_p':d_ps, 'cg':cgs}The %precision magic makes it easy to round all float on print to 4 decimal places.
%precision 4'%.4f'
returns_2(p=prices, d=dividends){'r': [0.5100, -0.3267, -0.4900, 1.0400, 0.5200, -0.3200, 0.5200],
'd_p': [0.0100, 0.0067, 0.0100, 0.0400, 0.0200, 0.0133, 0.0200],
'cg': [0.5000, -0.3333, -0.5000, 1.0000, 0.5000, -0.3333, 0.5000]}
By returning a dictionary instead of tuple, we can index returns, dividend yields, and capital gains yields by name instead of position.
returns_2(p=prices, d=dividends)['r'][0.5100, -0.3267, -0.4900, 1.0400, 0.5200, -0.3200, 0.5200]
[0, 1].Input: [18.5, 17.0, 18.0, 19.0, 18.0]
Output: [0.75, 0.0, 0.5, 1.0, 0.5]
x = [18.5, 17.0, 18.0, 19.0, 18.0]def rescale(x):
x_min = min(x)
x_max = max(x)
return [(i - x_min) / (x_max - x_min) for i in x]rescale(x)[0.7500, 0.0000, 0.5000, 1.0000, 0.5000]