Python strange syntaxis - python

I have been learning about ANN but the book I'm reading has examples in Python. The problem is that I have never written in Python and these lines of code are too hard for me to understand:
sizes = [3,2,4]
self.weights = [np.random.randn(y, x)
for x, y in zip(sizes[:-1], sizes[1:])]
I read some things about it and found out that the randn() function returns an array with y elements and x dimensions populated with random numbers between 0 and 1. zip() connects two arrays into one. sizes[:-1] returns the last element and sizes[1:] return the array without its first element.
But with all of this I still can't explain to myself what would this generate.

sizes[:-1] will return the sublist [3,2] (that is, all the elements except the last one).
sizes[1:] will return the sublist [2,4] (that is, all the elements except the first one).
zip([a,b], [c,d]) gives [(a,c), (b,d)].
So zipping the two lists above gives you [(3,2), (2,4)]
The construction of weights is a list comprehension. Therefore this code is equivalent to
weights = []
for x,y in [(3,2), (2,4)]:
weights.append(np.random.randn(y, x))
So the final result would be the same as
[ np.random.randn(2,3),
np.random.randn(4,2) ]

Let's break this up into chunks:
self.weights = [some junk]
is going to be a list comprehension. Meaning, do the some junk stuff and you'll end up with a list of elements from that. Usually these look like so:
self.weights = [some_func(x) for x in a_list]
This is the equivalent of:
self.weights = []
for x in a_list:
self.weights.append(some_func(x))
zip(a, b)
Will piecewise combine the elements of a and b into tuple pairs:
(a1, b1), (a2, b2), (a3, b3), ...
for x, y in zip(a, b):
This iterates through that tuple pairs talked about above
sizes[:-1]
This is stating to get all the elements of list sizes except the last item (-1).
sizes[1:]
This is stating to get the all the elements of list sizes except the first item.
So, finally piecing this all together you get:
self.weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]
Which is a list comprehension iterating through the tuple pairs of first sizes from 2nd item to last and second from 1st item to next to last, create a random number based on those two parameters, then append to a list that is stored as self.weights

a lot is going on here.
let's decompose that expression: as you said zip will create a list of tuples containing each element of sizes and it's successor (except for the last one)
The comprehension list [ ... for x, y in zip(..)] works as follows: the tuple is exploded in the variables x and y and those are passed onto np.random.randn to create a list of random matrices.
These matrices are characterized by having the first dimension (rows) long as specified by each element of sizes and the second dimension (columns) long as the following element.
Interestingly, the matrices have compatible dimensions to be multiplied to each other in that sequence, but I guess that this is not the purpose. The purpose of each matrix in the weights list is to specify the weights that are between fully connected layers of neurons. Good luck! Seems a fun project!
Post Scriptum
since you are a beginner: you can add the import pdb; pdb.set_trace() statement anywhere in your code to get a breakpoint. Then you can just copy and paste different parts of any expression to see what comes out.
For example:
ipdb> print sizes
[3, 2, 4]
ipdb> print sizes[:-1]
[3, 2]
ipdb> print sizes[1:]
[2, 4]
ipdb> print zip(sizes[:-1], sizes[1:])
[(3, 2), (2, 4)]
ipdb> print [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]
[array([[ 0.25933943, 0.59855688, 0.49055744],
[ 0.94602292, -0.8012292 , 0.56352986]]), array([[ 0.81328847, -0.53234407],
[-0.272656 , -1.24978881],
[-1.2306653 , 0.56038948],
[ 1.15837792, 1.19408038]])]

This code generates a list and assignes it to the self.weights attribute (this is maybe inside a class? That would explain the self). The second line is a list comprehension. It generates a list, applying the function randn to pairs of variables (x, y)

Related

How to replace values in nested array with values from a list?

Consider the variable "A" and an array with several nested arrays. Both "A" and the nested arrays contain the same amount of elements, in this case 5. Each nested array are also nested together in groups of 3.
A=[10,20,30,40,50]
array=[[[1,5,8,3,4],[18,4,-8,4,21],[-8,12,42,16,-9]], ...]
I was wondering how I can replace the elements in each nested array with the corresponding elements in A if the value of the element in the nested array exceeds a certain threshold. Otherwise, if the element fails to exceed the threshold, replace with zero.
For example, if the threshold is 10 in this example, the result would be:
array=[[[0,0,0,0,0],[10,0,0,0,50],[0,20,30,40,0]], ...]
I know this might be a simple problem, but I'm having trouble comprehending multidimensional arrays, especially if they are greater than 2-dimensions. The bigger question is, how would I do this if those arrays are nested under many arrays without using several for loops? My incorrect attempt:
for a in array:
for x in a:
for i in x:
if a[i]>10:
a[i]=A[i]
else:
a[i]=0
Your attempt is not working because first of all you are using the list value i as an index. In the line for i in x: the variable i will take each value of the list x, and if you need the index of it as well, you can use for id, i in enumerate(x) which gives you each value of the list as i and its index as id.
Moreover, to update the array, it is not enough to update x inside the loop, you need to update the array directly. And of course you can use list comprehension for simplicity in the last loop. So a solution to your problem could look like this:
for i1, val1 in enumerate(array):
for i2, val2 in enumerate(val1):
array[i1][i2] = [y if x>10 else 0 for (x, y) in zip(val2, A)]
As for your bigger question, the general solution when you have multiple nested lists and you don't want to use for loops is to implement recursive functions.
Here, one recursive solution to your problem would be:
def my_recursive_fun(input):
if isinstance(input, list) and isinstance(input[0], list):
return [my_recursive_fun(item) for item in input]
else:
return [y if x>10 else 0 for (x, y) in zip(input, [10,20,30,40,50])]
array=[[[1,5,8,3,4],[18,4,-8,4,21],[-8,12,42,16,-9]]]
new_array = my_recursive_fun(array)
The good thing about recursive solution is that it works with any number of nested lists (of course there are limits) without changing the code.
If the nesting of your array is arbitrary deep, then go for a recursive function:
def apply_threshold(substitutes, limit, source):
if isinstance(source[0], list): # recursive case
return [apply_threshold(substitutes, limit, elem) for elem in source]
else: # base case
return [subst if value >= limit else 0
for subst, value in zip(substitutes, source)]
Here is how to use it:
A = [10,20,30,40,50]
array = [[[1,5,8,3,4],[18,4,-8,4,21],[-8,12,42,16,-9]]]
result = apply_threshold(A, 10, array)
print(result) # [[[0, 0, 0, 0, 0], [10, 0, 0, 0, 50], [0, 20, 30, 40, 0]]]

How to do this specific list comprehension in Python?

points = [
[[x,y], [x,y], [x,y]],
[[x,y], [x,y], [x,y], [x,y], [x,y]],
[[x,y]]
]
weights = [1, 2, 3]
output = [
[[x,y,1], [x,y,1], [x,y,1]],
[[x,y,2], [x,y,2], [x,y,2], [x,y,2], [x,y,2]],
[[x,y,3]]
]
I want to combine Points and Weights to ultimately look like the output column. The length of points and weights will always be the same. However, the amount of [x,y] pairs will differ for each list inside the list. Exactly how I have shown the example. Python is the language I am using.
Any help would be greatly appreciated
Using zip it's relatively easy:
[[p+[w] for p in pl] for pl, w in zip(points, weights)]
Work from the inside out.
On the inside, we have [[x, y], [x, y], [x, y]] and 1, and we want to produce [[x, y, 1], [x, y, 1], [x, y, 1]].
So, we are appending 1 to each of the [x, y] values. But .append modifies a value in-place and returns None; to do work with list-comprehensions, we want to return new values. So we can instead create [1] from the input 1, and concatenate lists with +.
Therefore, we want the given inner item, + an appended list [1], for each of the items that is found in our original data ([[x, y], [x, y], [x, y]]). Since we are going to apply this to nested lists, let's say that we refer to that list as a row. Then, we write the list comprehension, describing that task - exactly as I put it, reading left to right: [item + [1] for item in row].
(Edit: as noted in the other answer, it also works to take advantage of unpacking: instead of item + [1], we can do [*item, 1].)
Now: that is the sort of processing that we want to do to each row. But we want to use a different value instead of the 1 each time. Those values come from weights, which we want to iterate in parallel with the rows of the overall points.
So, we need to use zip in order to pair up each weight of the weights with a corresponding row from points. That looks like zip(points, weights), and when we iterate over that, we get (row, weight) pairs.
Our processing for a given row now looks like [item + [weight] for item in row], after modifying it to account for the varying weight.
So we apply the technique again: we want to do [item + [weight] for item in row], for each of the (row, weight) pairs (parentheses are not strictly necessary here, but make things a lot easier to understand) that are found in our zipped lists of points and weights. And again, we read that left to right:
[[item + [weight] for item in row] for (row, weight) in zip(points, weights)]
Try this one-liner list comprehension without using a zip -
[[k+[weights[i]] for k in j] for i,j in enumerate(points)]

Change the first column of the matrix from another specified matrix

I have 2 matrices
x = [[1,2,3],
[4,5,6],
[7,8,9]]
y = [0,2,4]
and i want to change each first element from each row of matrix x using each element from matrix y so the end result would be
x = [[0,2,3],
[2,5,6],
[4,8,9]]
i have tried this code
x = [[1,2,3],[4,5,6],[7,8,9]]
y = [0,2,4]
for i in range (len(x)):
x[i][0] = y[0][i]
print (x)
but it only returns "TypeError: 'int' object is not subscriptable"
are there any ways to fix this and how do you expand this so that it's appliable to any n*n matrix?
Change x[i][0] = y[0][i] to x[i][0] = y[i].
Another way to do this with fewer indices:
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
y = [0, 2, 4]
for x_row, y_int in zip(x, y):
x_row[0] = y_int
print(x)
You do not have matrices. x is a list of lists and y is a list. They can represent matrices/vectors/etc., but those are ultimately mathematical abstractions, which can be implemented in code in different ways.
The first way to do it, maintaining the structure of your code, requires taking note of the above fact: as y is a list containing ints, y[0][i] will clearly not work, since y[0] will always be an int, and you cannot further apply the subscript operator to ints.
Accordingly, this will work:
for i in range (len(x)):
x[i][0] = y[i]
That said, that is not the only way to do it. If you desired a more functional approach, you could do something like this list comprehension:
[[y_value, *x_value[1:]] for x_value, y_value in zip(x, y)]
This gives the same result, but approaches the problem in a more abstract way: the new list will itself contain lists where the first element comes from y and the rest from x. Understanding this, we can instead compose an inner list following this pattern.
zip creates an iterator of pairs of values from x and y. Using this iterator, each value from y can be positioned before each value from x in a list. Lastly, since x_value is a list, it must be unpacked so we get, for example, [0, 2, 4] instead of [0, [2, 4]].
With Python, you would typically avoid using indexes when possible. Since a "matrix" is actually a list of lists, going through rows returns lists which are object references that you can manipulate directly:
for row,value in zip(x,y): row[0] = value

Python for loop and arrays

I'm new to python. I'm trying to learn data extracting from an Excel file. I encountered the following statement:
sheet_data = [[sheet.cell_value(r, col) for col in range(sheet.ncols)] for r in range(sheet.nrows)]
I understand regular for loops, but not the below version:
x for y in range()
What does it mean when you have a variable x before the for y in range()?
The for statement is used for looping over a list. This is referred to as an iterator. When it is encompassed by [..], this is referred to as a list comprehension.
List comprehensions allow you to transform one list into another. This is sometimes referred to as a mapping i.e. mapping from X -> Y where a function transforms the value of X into the returned value of Y
So, for example, in
[y + 2 for y in range(...)]
the for is iterating over all values in the list produced by the range(). Each list element has 2 added to each value of y, so the final result is a list where each element is 2 greater than the corresponding element in the source list. Thus, range(3) would produce [0, 1, 2] which then transforms into [2, 3, 4].
So [y for y in range(..)] wouldn't actually accomplish much.
I see that in the example you have provided there are two iterators, which complicates things a bit. But essentially, they are providing two reference variables: r and col, from which the final result is derived using these two variables.
List comprehensions are a very powerful tool in Python. Definitely worth knowing.
These are called list comprehensions in python. If you have a function do_something then the following two blocks are equivalent:
result = [do_something(y) for y in range(10)]
...
result = []
for y in range(10):
result.append(do_something(y))
Where range(10) could be any iterable.
Think of them as quick ways to create lists. They work for dictionaries too as of python 2.7. This tutorial may be helpful.
The "x" is an arbitrary variable name that holds the values of the sequence object. Using it in a list comprehension or in a generator expression will return the items in the iterable object that is being stepped through.

Accessing grouped items in arrays

I'm new to Python and have a list of numbers. e.g.
5,10,32,35,64,76,23,53....
and I've grouped them into fours (5,10,32,35, 64,76,23,53 etc..) using the code from this post.
def group_iter(iterator, n=2, strict=False):
""" Transforms a sequence of values into a sequence of n-tuples.
e.g. [1, 2, 3, 4, ...] => [(1, 2), (3, 4), ...] (when n == 2)
If strict, then it will raise ValueError if there is a group of fewer
than n items at the end of the sequence. """
accumulator = []
for item in iterator:
accumulator.append(item)
if len(accumulator) == n: # tested as fast as separate counter
yield tuple(accumulator)
accumulator = [] # tested faster than accumulator[:] = []
# and tested as fast as re-using one list object
if strict and len(accumulator) != 0:
raise ValueError("Leftover values")
How can I access the individual arrays so that I can perform functions on them. For example, I'd like to get the average of the first values of every group (e.g. 5 and 64 in my example numbers).
Let's say you have the following tuple of tuples:
a=((5,10,32,35), (64,76,23,53))
To access the first element of each tuple, use a for-loop:
for i in a:
print i[0]
To calculate average for the first values:
elements=[i[0] for i in a]
avg=sum(elements)/float(len(elements))
Ok, this is yielding a tuple of four numbers each time it's iterated. So, convert the whole thing to a list:
L = list(group_iter(your_list, n=4))
Then you'll have a list of tuples:
>>> L
[(5, 10, 32, 35), (64, 76, 23, 53), ...]
You can get the first item in each tuple this way:
firsts = [tup[0] for tup in L]
(There are other ways, of course.)
You've created a tuple of tuples, or a list of tuples, or a list of lists, or a tuple of lists, or whatever...
You can access any element of any nested list directly:
toplist[x][y] # yields the yth element of the xth nested list
You can also access the nested structures by iterating over the top structure:
for list in lists:
print list[y]
Might be overkill for your application but you should check out my library, pandas. Stuff like this is pretty simple with the GroupBy functionality:
http://pandas.sourceforge.net/groupby.html
To do the 4-at-a-time thing you would need to compute a bucketing array:
import numpy as np
bucket_size = 4
n = len(your_list)
buckets = np.arange(n) // bucket_size
Then it's as simple as:
data.groupby(buckets).mean()

Categories

Resources