list comprehension variable assignment [duplicate] - python

This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 3 years ago.
I am attempting to create a 4d array and assign variables to each of the cells.
Typically I would use four "for loops" but this is very messy and takes up a lot of space.
What i'm currently doing:
for x in range(2):
for y in range(2):
for j in range(2):
for k in range(2):
array[x,y,j,k] = 1 #will be a function in reality
I've tried using list comprehension but this only creates the list and does not assign variables to each cell.
Are there space-efficient ways to run through multiple for loops and assign variables with only a few lines of code?

Assuming you've already created an empty (numpy?) array, you can use itertools.product to fill it with values:
import itertools
for x, y, j, k in itertools.product(range(2), repeat=4):
arr[x,y,j,k] = 1
If not all of the array's dimensions are equal, you can list them individually:
for x, y, j, k in itertools.product(range(2), range(2), range(2), range(2)):
arr[x,y,j,k] = 1

You may however be wondering how itertools.product does the trick. Or maybe you want to encode a different transformation in your recursive expansion. Below, I'll share one possible solution using Python's generators -
def product (*iters):
def loop (prod, first = [], *rest):
if not rest:
for x in first:
yield prod + (x,)
else:
for x in first:
yield from loop (prod + (x,), *rest)
yield from loop ((), *iters)
for prod in product ("ab", "xyz"):
print (prod)
# ('a', 'x')
# ('a', 'y')
# ('a', 'z')
# ('b', 'x')
# ('b', 'y')
# ('b', 'z')
Because product accepts a a list of iterables, any iterable input can be used in the product. They can even be mixed as demonstrated here -
print (list (product (['#', '%'], range (2), "xy")))
# [ ('#', 0, 'x')
# , ('#', 0, 'y')
# , ('#', 1, 'x')
# , ('#', 1, 'y')
# , ('%', 0, 'x')
# , ('%', 0, 'y')
# , ('%', 1, 'x')
# , ('%', 1, 'y')
# ]
We could make a program foo that provides the output posted in your question -
def foo (n, m):
ranges = [ range (m) ] * n
yield from product (*ranges)
for prod in foo (4, 2):
print (prod)
# (0, 0, 0, 0)
# (0, 0, 0, 1)
# (0, 0, 1, 0)
# (0, 0, 1, 1)
# (0, 1, 0, 0)
# (0, 1, 0, 1)
# (0, 1, 1, 0)
# (0, 1, 1, 1)
# (1, 0, 0, 0)
# (1, 0, 0, 1)
# (1, 0, 1, 0)
# (1, 0, 1, 1)
# (1, 1, 0, 0)
# (1, 1, 0, 1)
# (1, 1, 1, 0)
# (1, 1, 1, 1)
Or use destructuring assignment to create bindings for individual elements of the product. In your program, simply replace print with your real function -
for (w, x, y, z) in foo (4, 2):
print ("w", w, "x", x, "y", y, "z", z)
# w 0 x 0 y 0 z 0
# w 0 x 0 y 0 z 1
# w 0 x 0 y 1 z 0
# w 0 x 0 y 1 z 1
# w 0 x 1 y 0 z 0
# w 0 x 1 y 0 z 1
# w 0 x 1 y 1 z 0
# w 0 x 1 y 1 z 1
# w 1 x 0 y 0 z 0
# w 1 x 0 y 0 z 1
# w 1 x 0 y 1 z 0
# w 1 x 0 y 1 z 1
# w 1 x 1 y 0 z 0
# w 1 x 1 y 0 z 1
# w 1 x 1 y 1 z 0
# w 1 x 1 y 1 z 1
Because product is defined as a generator, we are afforded much flexibility even when writing more complex programs. Consider this program that finds right triangles made up whole numbers, a Pythagorean triple. Also note that product allows you to repeat an iterable as input as see in product (r, r, r) below
def is_triple (a, b, c):
return a * a + b * b == c * c
def solver (n):
r = range (1, n)
for p in product (r, r, r):
if is_triple (*p):
yield p
print (list (solver (20)))
# (3, 4, 5)
# (4, 3, 5)
# (5, 12, 13)
# (6, 8, 10)
# (8, 6, 10)
# (8, 15, 17)
# (9, 12, 15)
# (12, 5, 13)
# (12, 9, 15)
# (15, 8, 17)
For additional explanation and a way to see how to do this without using generators, view this answer.

Related

Getting all possible combination for [1,0] with length 3 [0,0,0] to [1,1,1]

from itertools import combinations
def n_length_combo(arr, n):
# using set to deal
# with duplicates
return list(combinations(arr, n))
# Driver Function
if __name__ == "__main__":
arr = '01'
n = 3
print (n_length_combo([x for x in arr], n) )
Expected Output
wanted 3 combination of 0 and 1 .Tried with above example but it is not working
You're looking for a Cartesian product, not a combination or permutation of [0, 1]. For that, you can use itertools.product.
from itertools import product
items = [0, 1]
for item in product(items, repeat=3):
print(item)
This produces the output you're looking for (albeit in a slightly different order):
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 0)
(1, 1, 1)

Aggregating groups into row vectors (rather than scalars)

I want to apply a function to every group in a groupby object, so that the function operates on multiple columns of each group, and returns a 1 x n "row vector" as result. I want the n entries of these row vectors to form the contents of n new columns in the resulting DataFrame.
Here's an example.
import pandas as pd
import numpy as np
df = pd.DataFrame.from_records([(0, 0, 0.616, 0.559),
(0, 0, 0.976, 0.942),
(0, 0, 0.363, 0.223),
(0, 0, 0.033, 0.225),
(0, 0, 0.950, 0.351),
(0, 1, 0.272, 0.004),
(0, 1, 0.167, 0.177),
(0, 1, 0.520, 0.157),
(0, 1, 0.435, 0.547),
(0, 1, 0.266, 0.850),
(1, 0, 0.368, 0.544),
(1, 0, 0.067, 0.064),
(1, 0, 0.566, 0.533),
(1, 0, 0.102, 0.431),
(1, 0, 0.240, 0.997),
(1, 1, 0.867, 0.793),
(1, 1, 0.519, 0.477),
(1, 1, 0.110, 0.853),
(1, 1, 0.160, 0.155),
(1, 1, 0.735, 0.515)],
columns=list('vwxy'))
grouped = df.groupby(list('vw'))
def example(group):
X2 = np.var(group['x'])
Y2 = np.var(group['y'])
X = np.sqrt(X2)
Y = np.sqrt(Y2)
R2 = X2 + Y2
M = 1.0/(R2 + 1)
return (M * 2 * X, M * 2 * Y, M * (R2 - 1))
This gets close:
grouped.apply(example).reset_index()
# v w 0
# 0 0 0 (0.596122357697, 0.450073544336, -0.664884906839)
# 1 0 1 (0.229241003533, 0.555057863705, -0.799599481139)
# 2 1 0 (0.326212671335, 0.53100544639, -0.782060425392)
# 3 1 1 (0.523276087715, 0.433768876798, -0.733503031723)
...but what I'm after is this:
# v w a b c
# 0 0 0 0.596122 0.450074 -0.664885
# 1 0 1 0.229241 0.555058 -0.799599
# 2 1 0 0.326213 0.531005 -0.782060
# 3 1 1 0.523276 0.433769 -0.733503
How can I achieve this?
It's OK to modify the example function, as long as it continues to return all 3 values in some form. IOW, I don't want a solution based on replacing example with 3 separate functions, one for each of the output columns.
Try returning a pandas Series instead of a tuple from example:
def example(group):
....
return pd.Series([M * 2 * X, M * 2 * Y, M * (R2 - 1)], index=list('abc'))

Number of pairs [duplicate]

I am trying to write a code that takes
m. a, a list of integers
n. b, an integer
and returns the number of pairs (m,n) with m,n in a such that |m-n|<=b.
So far, I've got this
def nearest_pairs(a, b):
m= []
n= int
num_pairs = 0
return num_pairs
def main():
# The nearest pairs are (1,2), (2,1), (2,5) and (5,2)
x = nearest_pairs( [1,2,5] , 3 )
print( "nearest_pairs([1, 2, 5], 3) = " , nearest_pairs([1, 2, 5], 3) )
# The nearest pairs are (1,2) and (2,1)
y = nearest_pairs( [1, 2, 5] , 2 )
print( "nearest_pairs([1, 2, 5], 2) = " , nearest_pairs([1, 2, 5], 2) )
if __name__ == '__main__':
main()
The desired output should look like
>>> nearest_pairs([1,2,5],3) = 4
where 4 is the number of close pairs according to the restrictions. However, I get an error. Could anyone lead me to the right direction?
Yours doesn't make sense. No idea what you're trying with len(a, b), but it's not even allowed, since len takes only one argument. And returning something just when you found the first counting pair? Here's a fix:
def close_pairs(l, d):
ctr = 0
for a,b in permutations(l, 2):
if (a - b) <= d and (b - a) <= d:
ctr += 1
return ctr
And here's how I'd do it:
def close_pairs(l, d):
return sum(abs(a-b) <= d for a, b in permutations(l, 2))
from itertools import permutations
def nearest_pairs(a, b):
for m, n in permutations(a, 2):
if abs(m - n) <= b:
yield (m, n)
>>> list(nearest_pairs([1, 2, 5], 3))
[(1, 2), (2, 1), (2, 5), (5, 2)]
>>> list(nearest_pairs([1, 2, 5], 2))
[(1, 2), (2, 1)]
If you just want the count:
def nearest_pairs_count(a, b):
c, l = 0, len(a)
for i in range(l):
for j in range(i + 1, l):
if abs(a[i] - a[j]) <= b:
c += 2
return c

Python, finding neighbors in a 2-d list

So here's the issue, I have a 2-d list of characters 'T' and 'F', and given coordinates I need to get all of its neighbors. I have this:
from itertools import product, starmap
x, y = (5, 5)
cells = starmap(lambda a, b: (x + a, y + b), product((0, -1, +1), (0, -1, +1)))
from determining neighbors of cell two dimensional list But it will only give me a list of coordinantes, so i still fetch the values afterwords. I'd like the search and retrieval done in one step, so findNeighbors(5,5) would return F,T,F,F,... instead of (5, 4), (5, 6), (4, 5), (4, 4)... Is there a fast way of doing this? The solutin can include a structure other than a list to hold the initial information
The following should work, with just a minor adaptation to the current code:
from itertools import product, starmap, islice
def findNeighbors(grid, x, y):
xi = (0, -1, 1) if 0 < x < len(grid) - 1 else ((0, -1) if x > 0 else (0, 1))
yi = (0, -1, 1) if 0 < y < len(grid[0]) - 1 else ((0, -1) if y > 0 else (0, 1))
return islice(starmap((lambda a, b: grid[x + a][y + b]), product(xi, yi)), 1, None)
For example:
>>> grid = [[ 0, 1, 2, 3],
... [ 4, 5, 6, 7],
... [ 8, 9, 10, 11],
... [12, 13, 14, 15]]
>>> list(findNeighbors(grid, 2, 1)) # find neighbors of 9
[8, 10, 5, 4, 6, 13, 12, 14]
>>> list(findNeighbors(grid, 3, 3)) # find neighbors of 15
[14, 11, 10]
For the sake of clarity, here is some equivalent code without all of the itertools magic:
def findNeighbors(grid, x, y):
if 0 < x < len(grid) - 1:
xi = (0, -1, 1) # this isn't first or last row, so we can look above and below
elif x > 0:
xi = (0, -1) # this is the last row, so we can only look above
else:
xi = (0, 1) # this is the first row, so we can only look below
# the following line accomplishes the same thing as the above code but for columns
yi = (0, -1, 1) if 0 < y < len(grid[0]) - 1 else ((0, -1) if y > 0 else (0, 1))
for a in xi:
for b in yi:
if a == b == 0: # this value is skipped using islice in the original code
continue
yield grid[x + a][y + b]

Can someone explain this program to me?

What does the following expression produce as a value:
[(x, x*y) for x in range(2) for y in range(2)]
[(0,0), (0,1), (1,0), (1,1)]
[0, 1, 2]
[(0,0), (1,0), (0,0), (1,1)]
[(0,0), (0,0), (1,0), (1,1)]
None of the above
The answer is 4, but I don't understand why.
Assuming python 2.
range(2) returns the list [0, 1]
[(x, x*y) for x in [0, 1] for y in [0,1]]
Thus x and y will be all combinations of the lists [0, 1] and [0, 1]
[(x, x*y) for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]]
x y x*y (x, x*y)
0 0 0 (0, 0)
0 1 0 (0, 0)
1 0 0 (1, 0)
1 1 1 (1, 1)
read as:
for x in range(2): # 0,1
for y in range(2): # 0,1
(x, x*y)
Read this as
list = [];
for x in range(2):
for y in range(2):
list.append((x, x*y))
Basically it will iterate 4 times with the following X,Y values
X=0, Y=0
X=0, Y=1
X=1, Y=0
X=1, Y=1
Zero times anything will always be zero, so you get your 4 arrays
First Index = 0, 0*0
Second Index = 0, 0*1
Third Index = 1, 1*0
Fourth Index = 1, 1*1
Nested list comprehensions work in the same way as if you had written for loops like that.
So your example list comprehension works like this generator function:
def example():
for x in range(2):
for y in range(2):
yield (x, x*y)

Categories

Resources