What's an elegant way to map func over each value in a 2D list?
The best I have so far:
result = [[func(value) for value in row] for row in grid]
It's a shame I have to nest square brackets, and have a lot of list comprehensions. Is there a less verbose or more elegant way?
result = map2d(func, grid)
"It's a shame I have to nest square brackets" - No it's not, it's a Pythonic way to go and makes it clear that result is a list of lists (as long as you don't go overboard with even more nested list comprehensions). If you really don't want to see that in the code just wrap it in your own function:
def map2d(func, grid):
return [[func(value) for value in row] for row in grid]
result = map2d(func, grid)
I think your initial approach is obvious, readable, error free and short enough for the task at hand. It can be refactored to a "map2d" func, but then, just create this func and put that code in, like in: map2d= lambda func, data: [[func(element) for element in row] for row in data]
What might feel strange about nestign the brackets is that the "list inside list" approach for 2D data, although straightforward, have some drawbacks.
It is easy to have other forms of 2D data structures in Python, by implementing a new class with custom __getitem__, __setitem__ and __delitem__ methods that will give allow the syntax mydata[x, y]. And then, if you derive your class, for example, from collections.abc.Mapping, you can iterate over it, or over the return of mydata.itemsto reach all data points with a linear for loop.
another advantage is that a custom class can them know about its width X height and check boundaries - and just return a default value for unfilled elments, working as a sparse matrix.
Another approach is simply using a dictionary, with no class creation, for your 2D data, and retrieve data with the get method in order to have a default value at hand:
from random import randint
W, H = 10, 10
grid = dict()
# fill in data:
for x in range(W):
for y in range(H):
grid[x,y] = randint(0, 100)
# loop through all values:
for coord in grid:
print(grid[coord])
# change value:
grid[coord] = new_value(grid[coord])
This approach won't let you do "map" with a onelinet, but by explictly calling in grid.__setitem__ though. By using a custom class, like suggested above, you can simply add a .map method that will do that.
You could use mal twice, not sure though if this is more elegant as compared to your solution
result = list(map(lambda row: list(map(func, row)), grid))
There's nothing wrong about the nested listcomp, it is my preferred solution. But you could "vectorize" the function you want to apply.
def vectorize(f):
def f_vectorized(args): # iterable args, check against str in real code
return [f(x) for x in args]
return f_vectorized
func_vec = vectorize(func)
result = list(map(func_vec, grid))
Demo:
>>> def func(x):
... return x + 1
>>> grid = [[1, 2], [3, 4]]
>>> func_vec = vectorize(func)
>>> func_vec_vec = vectorize(func_vec)
>>> list(map(func_vec, grid))
[[2, 3], [4, 5]]
>>> func_vec_vec(grid)
[[2, 3], [4, 5]]
You can define your own -
def map2d(func, grid):
out_grid = []
for row in grid:
out_grid.append(list(map(func, row)))
return out_grid
x = [[1, 1, 2], [2, 2, 3], [3, 4, 3]]
map2d(lambda z: z**2, x)
#[[1, 1, 4], [4, 4, 9], [9, 16, 9]]
Related
I'm currently doing a school project where I need to code a program in python that subtracts a value from a list of coordinates.
I'm not allowed to use import to make things easier.
I have the list: [[1,2],[2,3],[3,4]]
and I need to subtract a value from the second value in each list inside this list.
So for an example: [[1,2],[2,3],[3,4]]
becomes: [[1,1],[2,2],[3,3]]
This is the code I have thus far:
def coordinates(y):
for i in (y):
(i[1] - 5)
return (y)
I just can't get it to work though. Does anyone know a good way to do this?
Edit:
ShyGuyRyRyNewlyTheDataGuy's code worked like a charm if anyone has the same problem!
def coordinates(l):
for i in range(len(l)):
l[i][1] = l[i][1] - 1
return l
Let's assume that you don't want to modify the original list. In that case:
def coordinates(l, n=1):
return [[x, y-n] for x, y in l]
print(coordinates([[1,2],[2,3],[3,4]]))
Output:
[[1, 1], [2, 2], [3, 3]]
Given you're attempting to always subtract from the y coordinate, indexing into the list's set of lists would provide the easiest solution to your problem.
The following subtracts 1 from the lists second coordinate and returns the list.
def coordinates(l):
for i in range(len(l)):
l[i][1] = l[i][1] - 1
return l
I think you should make your function generic by passing everything it needs to know to is as arguments instead of hardcoding everythings into it — which will allow it to be reused with different lists and/or amounts.
This following illustrates what I mean:
def add_y(coords, y):
for coord in coords:
coord[1] += y
return coords
data = [[1,2],[2,3],[3,4]]
print(data) # -> [[1, 2], [2, 3], [3, 4]]
data = add_y(data, -1)
print(data) # -> [[1, 1], [2, 2], [3, 3]]
In a function I am making, I have a for x in range(y) loop that will create a list with x list depth inside it.
For example at x=1, it will create [[a,b],[a,b]]
and at x=2, it will create [[[a,b],[a,b]],[[a,b],[a,b]]]
I want to be able to look at each element (each a and b), for example with x=1 do:
for small_list in list:
for element in small_list:
print(element)
but this is difficult for me to figure out because at each different x value, I would need to change the number of for statements. Is there a way to change the number of for statements for my list based on the changing number x, since x is linearly proportional to the number of for statements I need?
You could use a single argument expression being re-called over and over to wrap your iterable:
def wrap_iter(x):
return [x]
# then a function to recursively call f
def nums(f, x, rep):
while rep:
x = f(x)
rep-=1
return x
a = [[1,2], [1,2]]
nums(wrap_iter, a, 1)
[[[1, 2], [1, 2]]]
nums(wrap_iter, a, 0)
[[1, 2], [1, 2]]
Is there a way to change the number of for statements...
No. Instead, you should create a recursive function, i.e. a function that calls itself again for a smaller problem until it hits some kind of "base case".
You seem to be looking for some kind of Tree Traversal function; something like this:
def print_tree(tree):
if isinstance(tree, list):
for x in tree:
print_tree(x) # calling itself for smaller problem
else:
print(tree) # base case
print_tree([[[1,2],[3,4]],[[5,6],[7,8]]])
As an alternative to a recursive function, you can also traverse the structure keeping your own stack:
def traverse(tree):
stack = [tree]
while stack:
current = stack.pop()
if isinstance(current, list):
stack.extend(current[::-1])
else:
yield current
print(*traverse([[1, 2], [3, 4]]))
# 1 2 3 4
print(*traverse([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]))
# 1 2 3 4 5 6 7 8
So I'm finished one part of this assignment I have to do. There's only one part of the assignment that doesn't make any sense to me.
I'm doing a LinearRegression model and according to others I need to apply ans[i,:] = y_poly at the very end, but I never got an answer as to why.
Can someone please explain to me what [i,:] means? I haven't found any explanations online.
It's specific to the numpy module, used in most data science modules.
ans[i,:] = y_poly
this is assigning a vector to a slice of numpy 2D array (slice assignment). Self-contained example:
>>> import numpy
>>> a = numpy.array([[0,0,0],[1,1,1]])
>>> a[0,:] = [3,4,5]
>>> a
array([[3, 4, 5],
[1, 1, 1]])
There is also slice assignment in base python, using only one dimension (a[:] = [1,2,3])
I guess you are also using numpy to manipulate data (as a matrix)?
If based on numpy, ans[i,:] means to pick the ith 'row' of ans with all of its 'columns'.
Note: when dealing with numpy arrays, we should (almost) always use [i, j] instead of [i][j]. This might be counter-intuitive if you've used Python or Java to manipulate matrixes before.
I think in this case [] means the indexing operator for a class object which can be used by defining the getitem method
class A:
def __getitem__(self, key):
pass
key can be literally anything. In your case "[1,:]" key is a tuple containing of "1" and a slice(None, None, None). Such a key can be useful if your class represents multi-dimensional data which you want to access via [] operator. A suggested by others answers this could be a numpy array:
Here is a quick example of how such a multi-dimensional indexing could work:
class A:
values = [[1,2,3,4], [4,5,6,7]]
def __getitem__(self, key):
i, j = key
if isinstance(i, int):
i = slice(i, i + 1)
if isinstance(j, int):
j = slice(j, j + 1)
for row in self.values[i]:
print(row[j])
>>>a = A()
>>>a[:,2:4]
[3, 4]
[6, 7]
>>>a[1,1]
[5]
>>>a[:, 2]
[3]
[6]
I am working through some code trying to understand some Python mechanics, which I just do not get. I guess it is pretty simple and I also now, what it does, but i do not know how it works. I understand the normal use of for-loops but this here... I do not know.
Remark: I know some Python, but I am not an expert.
np.array([[[S[i,j]] for i in range(order+1)] for j in range(order+1)])
The second piece of code, I have problems with is this one:
for i in range(len(u)):
for j in range(len(v)):
tmp+=[rm[i,j][k]*someFuction(name,u[i],v[j])[k] for k in range(len(rm[i,j])) if rm[i,j][k]]
How does the innermost for-loop work? And also what does the if do here?
Thank you for your help.
EDIT: Sorry that the code is so unreadable, I just try to understand it myself. S, rm are numpy matrices, someFunction returns an array with scalar entries, andtmp is just a help variable
There are quite a few different concepts inside your code. Let's start with the most basic ones. Python lists and numpy arrays have different methodologies for indexation. Also you can build a numpy array by providing it a list:
S_list = [[1,2,3], [4,5,6], [7,8,9]]
S_array = np.array(S_list)
print(S_list)
print(S_array)
print(S_list[0][2]) # indexing element 2 from list 0
print(S_array[0,2]) # indexing element at position 0,2 of 2-dimensional array
This results in:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1 2 3]
[4 5 6]
[7 8 9]]
3
3
So for your first line of code:
np.array([[[S[i,j]] for i in range(order+1)] for j in range(order+1)])
You are building a numpy array by providing it a list. This list is being built with the concept of list comprehension. So the code inside the np.array(...) method:
[[[S[i,j]] for i in range(order+1)] for j in range(order+1)]
... is equivalent to:
order = 2
full_list = []
for j in range(order+1):
local_list = []
for i in range(order+1):
local_list.append(S_array[i, j])
full_list.append(local_list)
print(full_list)
This results in:
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
As for your second snippet its important to notice that although typically numpy arrays have very specific and constant (for all the array) cell types you can actually give the data type object to a numpy array. So creating a 2-dimensional array of lists is possible. It is also possible to create a 3-dimensional array. Both are compatible with the indexation rm[i,j][k]. You can check this in the following example:
rm = np.array(["A", 3, [1,2,3]], dtype="object")
print(rm, rm[2][0]) # Acessing element 0 of list at position 2 of the array
rm2 = np.zeros((3, 3, 3))
print(rm2[0, 1][2]) # This is also valid
The following code:
[rm[i,j][k]*someFuction(name,u[i],v[j])[k] for k in range(len(rm[i,j])) if rm[i,j][k]]
... could be written as such:
some_list = []
for k in range(len(rm[i,j])):
if rm[i, j][k]: # Expecting a boolean value (or comparable)
a_list = rm[i,j][k]*someFuction(name,u[i],v[j])
some_list.append(a_list[k])
The final detail is the tmp+=some_list. When you sum two list they'll be concatenated as can been seen in this simple example:
tmp = []
tmp += [1, 2, 3]
print(tmp)
tmp += [4, 5, 6]
print(tmp)
Which results in this:
[1, 2, 3]
[1, 2, 3, 4, 5, 6]
Also notice that multiplying a list by a number will effectively be the same as summing the list several times. So 2*[1,2] will result in [1,2,1,2].
Its a list comprehension, albeit a pretty unreadable one. That was someome doing something very 'pythonic' in spite of readablity. Just look up list comprehensions and try to rewrite it yourself as a traditional for loop. list comprehensions are very useful, not sure I would have gone that route here.
The syntax for a list comprehension is
[var for var in iterable if optional condition]
So this bottom line can be rewritten like so:
for k in range(len(rm[i,j]):
if rm[i,j][k]:
tmp+= rm[i,j][k]*someFunction(name,u[i],v[j])[k]
How to set the same value to the matrix of multiple rows and each row with different column numbers without for loop?
For example for matrix a:
a=matrix([[1,2,3],
[8,2,9],
[1,8,7]])
row = [1,2,3]
col = [[1,2]
[1,3]
[2,3]]
I want to set a[1,1],a[1,2],a[2,1],a[2,3],a[3,2],a[3,3] to the same value.
I know use for loop:
for i in xrange(len(row)):
a[row[i],col[i]] = setvalue
But is there anyway to do this without for loop?
Using numpy, you can avoid loops:
import numpy as np
from numpy.matlib import repmat
a = np.array([[1,2,3],
[8,2,9],
[1,8,7]])
row = np.array([[1],
[2],
[3]])
col = np.array([[1,2],
[1,3],
[2,3]])
row = repmat(row,1,col.shape[1])
setvalue = 0
a[row.ravel(),col.ravel()] = setvalue
However, it's important to note that in python indexing starts at 0, so you should actually do
a[row-1,col-1] = setvalue
Or even better, use the correct (zero-based) indices to initialise your row and col arrays.
Case 1: Use list comprehension
You can do like this:
value = 2
col_length = 3
line_length = 3
a = [[value for x in range(col_length)] for x in range(line_length)]
If you print a,
[[2, 2, 2], [2, 2, 2], [2, 2, 2]]
EDIT: Case 2 : Use map()
I am not very used to this one. But you can find more informations about it here in terms of performance. General idea: it seems faster when used with one function and no lambda expression.
You'll have to use a for loop.
Usually you want to avoid for loops (by using comprehesions) when following the functional paradigm, by building new instances instead of mutating the old one. As your goal is to mutate the old one, somewhere you will need a loop. The best you can do is to wrap it up in a function:
def set_items_to(mx, indices, value=0):
for row,cols in indices:
for col in cols:
mx[row, col] = value
a = matrix([[1,2,3],[4,5,6],[7,8,9]])
set_items_to(a, [
[0, [0,1]],
[1, [0,2]],
[2, [1,2]]
], setvalue)
EDIT
In case it is a programming challenge, there are ways to accomplish that without explicit for loops by using one of the built in aggregator functions. But this approach doesn't make the code clearer nor shorter. Just for completeness, it would look something like this:
def set_items_to(mx, indices, value=0):
sum(map(lambda item: [0,
sum(map(lambda col: [0,
mx.__setitem__((item[0], col), value)
][0], item[1]))
][0], indices))