Iterating over N dimensions in Python - python

I have a map, let's call it M, which contains data mapped through N dimensions.
# If it was a 2d map, I could iterate it thusly:
start, size = (10, 10), (3, 3)
for x in range(start[0], start[0]+size[0]):
for y in range(start[1], start[1]+size[1]):
M.get((x, y))
# A 3d map would add a for z in ... and access it thusly
M.get((x, y, z))
# And so on.
My question is: How do I make an iterator that can yield the correct iteration sequence? That is, given start, size = (10, 10), (3, 3) it would yield the 2-tuple sequence (10, 10), (10, 11), (10, 12), (11, 10), (11, 11) etc. And given start, size = (10, 10, 10), (3, 3, 3) it would yield the correct 3-tuple sequence.
Yeah, I tried myself, but my head exploded. Or I can't justify spending time figuring it out, even though it's fun. Take your pick :)

In Python 2.6+:
itertools.product(*[xrange(i, i+j) for i,j in zip(start, size)])

With do it your self generator expreessions:
start, size = (10, 10), (3, 3)
values2=((x+xd,y+yd)
for x,y in (start,)
for xr,yr in (size,)
for xd in range(xr)
for yd in range(yr))
for x,y in values2:
print x,y
start, size = (10, 10, 10), (3, 3, 3)
values3=((x+xd,y+yd, z+zd)
for x,y,z in (start,)
for xr,yr,zr in (size,)
for xd in range(xr)
for yd in range(yr)
for zd in range(zr))
for x,y,z in values3:
print x,y,z

Related

Getting the correct max value from a list of tuples

My list of tuples look like this:
[(0, 0), (3, 0), (3, 3), (0, 3), (0, 0), (0, 6), (3, 6), (3, 9), (0, 9), (0, 6), (6, 0), (9, 0), (9, 3), (6, 3), (6, 0), (0, 3), (3, 3), (3, 6), (0, 6), (0, 3)]
It has the format of (X, Y) where I want to get the max and min of all Xs and Ys in this list.
It should be min(X)=0, max(X)=9, min(Y)=0, max(Y)=9
However, when I do this:
min(listoftuples)[0], max(listoftuples)[0]
min(listoftuples)[1], max(listoftuples)[1]
...for the Y values, the maximum value shown is 3 which is incorrect.
Why is that?
for the Y values, the maximum value shown is 3
because max(listoftuples) returns the tuple (9, 3), so max(listoftuples)[0] is 9 and max(listoftuples)[1] is 3.
By default, iterables are sorted/compared based on the values of the first index, then the value of the second index, and so on.
If you want to find the tuple with the maximum value in the second index, you need to use key function:
from operator import itemgetter
li = [(0, 0), (3, 0), ... ]
print(max(li, key=itemgetter(1)))
# or max(li, key=lambda t: t[1])
outputs
(3, 9)
Here is a simple way to do it using list comprehensions:
min([arr[i][0] for i in range(len(arr))])
max([arr[i][0] for i in range(len(arr))])
min([arr[i][1] for i in range(len(arr))])
max([arr[i][1] for i in range(len(arr))])
In this code, I have used a list comprehension to create a list of all X and all Y values and then found the min/max for each list. This produces your desired answer.
The first two lines are for the X values and the last two lines are for the Y values.
Tuples are ordered by their first value, then in case of a tie, by their second value (and so on). That means max(listoftuples) is (9, 3). See How does tuple comparison work in Python?
So to find the highest y-value, you have to look specifically at the second elements of the tuples. One way you could do that is by splitting the list into x-values and y-values, like this:
xs, ys = zip(*listoftuples)
Or if you find that confusing, you could use this instead, which is roughly equivalent:
xs, ys = ([t[i] for t in listoftuples] for i in range(2))
Then get each of their mins and maxes, like this:
x_min_max, y_min_max = [(min(L), max(L)) for L in (xs, ys)]
print(x_min_max, y_min_max) # -> (0, 9) (0, 9)
Another way is to use NumPy to treat listoftuples as a matrix.
import numpy as np
a = np.array(listoftuples)
x_min_max, y_min_max = [(min(column), max(column)) for column in a.T]
print(x_min_max, y_min_max) # -> (0, 9) (0, 9)
(There's probably a more idiomatic way to do this, but I'm not super familiar with NumPy.)

How to print the non-intersecting intervals in increasing order of the size of the intervals?

def mergeoverlapping(initialranges):
i = sorted(set([tuple(sorted(x)) for x in initialranges]))
f = [i[0]]
for c, d in i[1:]:
a, b = f[-1]
if c<=b<d:
f[-1] = a, d
elif b<c<d:
f.append((c,d))
else:
pass
return f
def main():
#open file for reading
list_of_Tups = []
with open("intervals.txt") as in_file:
for line in in_file:
int_list = [int(i) for i in line.split()]
line = int_list
list_of_Tups.append(line)
list_of_Tups.sort()
answer = list(mergeoverlapping(list_of_Tups))
print("Non-intersecting Intervals:")
for i in range (len(answer)):
print(answer[i])
main()
Given a data file, I had to create tuples, store tuples in list, sort the list and replace each pair of overlapping tuples with a single tuple.Then print list of non intersecting tuples.
But now I want to know how to print the non-intersecting intervals in increasing order of the size of the intervals. If two intervals are of the same size then print the two intervals in ascending order of their lower ends. So the output would look like:
Non-intersecting Intervals:
(-4,3)
(4,7)
(10,15)
Non-intersecting Intervals in order of size:
(4, 7)
(10, 15)
(-4, 3)
Given:
intervals = [(-25, -14), (-10, -3), (2, 6), (12, 18), (22, 30)]
Then like you've already been sorting, you just sort it based on the size.
# First sort in ascending order in relation to lower
intervals.sort(key=lambda interval: interval[0])
# Then sort in ascending order in relation to size
intervals.sort(key=lambda interval: abs(interval[0] - interval[1]))
Then print(intervals) outputs:
[(2, 6), (12, 18), (-10, -3), (22, 30), (-25, -14)]
Thus given (-4, 3), (4, 7), (-2, 1) then it results in (-2, 1), (4, 7), (-4, 3) (size is 3, 3, 7). So it correctly sorts based on lower and then size.

How to create 10 random x, y coordinates in a grid using Python

I need to create a 8x8 grid and distribute 10 coins in random positions on the grid. The problem I am facing is that the randint function will sometimes generate the same random co-ordinates and therefore only 9 or 8 coins are generated and placed on the grid. How can I make sure this doesn't happen? Cheers :) This is my code so far:
from random import randint
grid = []
#Create a 8x8 grid
for row in range(8):
grid.append([])
for col in range(8):
grid[row].append("0")
#create 10 random treasure chests
#problem is that it might generate the same co-ordinates and therefore not enough coins
for coins in range(10):
c_x = randint(0, len(grid)-1)
c_y = randint(0, len(grid[0])-1)
while c_x == 7 and c_y == 0:
c_x = randint(0, len(grid)-1)
c_y = randint(0, len(grid[0])-1)
else:
grid[c_x][c_y] = "C"
for row in grid:
print(" ".join(row))
I have included a while/else - as there must not be a coin in the bottom left corner of the grid
You only have 64 cases, so you can generate all coordinates as tuples (x,y) and then you can use random.sample to directly have 10 unique elements, so you don't have to check or redraw.
import random
from itertools import product
g = [['0' for _ in range(8)] for _ in range(8)]
coord = list(product(range(8), range(8)))
for coins in random.sample(coord, 10):
g[ coins[0] ][ coins[1] ] = 'C'
for row in g:
print(' '.join(row))
So you wish to generate 10 random unique coordinates?
You can use a set to verify:
cords_set = set()
while len(cords_set) < 10:
x, y = 7, 0
while (x, y) == (7, 0):
x, y = randint(0, len(grid) - 1), randint(0, len(grid[0]) - 1)
# that will make sure we don't add (7, 0) to cords_set
cords_set.add((x, y))
This will generate a set of tuples that represent (x, y) coordinates.
A few examples of the output of print(cords_set):
{(5, 6), (7, 6), (4, 4), (6, 3), (7, 4), (6, 2), (3, 6), (0, 4), (1, 7), (5, 2)}
{(7, 3), (1, 3), (2, 6), (5, 5), (4, 6), (3, 0), (0, 7), (2, 0), (4, 1), (6, 5)}
{(1, 2), (1, 3), (6, 7), (3, 3), (4, 5), (4, 4), (6, 0), (1, 0), (2, 5), (2, 4)}
You could add another check in your while loop to make sure there is not already a coin at the currently chosen coordinate.
BTW, you could also avoid the checks you already have by changing the range of your randint directly to match your needs.
Or you could generate all possible 7*7=49 coordinates (eliminating the unwanted coordinates) and then pick 10 different at random using the np.random.choice function.
Look at the code below:
from random import randint
grid = []
#Create a 8x8 grid
for row in range(8):
grid.append([])
for col in range(8):
grid[row].append("0")
for coins in range(10):
c_x = randint(0, len(grid)-1)
c_y = randint(0, len(grid[0])-1)
while grid[c_x][c_y] == "C":
c_x = randint(0, len(grid) - 1)
c_y = randint(0, len(grid[0]) - 1)
grid[c_x][c_y] = "C"
The after generating the coordinates you check to make sure there is no 'C' in place before assigning one to it. If there is you draw again and re-check. If there is not, you assign one and draw the next.
Let me know if this helps ☺

Merging(2 lists) X Coordinate List and Y Coordinate List to Get(1 list) XY Coordinate List

I have a list of:
xvalues = [1,2,3,4,5,6,7,8,9,10]
and a list of:
yvalues = [n1,n2,n3,n4,n5,n6,n7,n8,n9,n10]
The y-values are random thats why they are being notated as they are.
I want to merge these two lists into one list like this:
xyvalues = [(1,n1),(2,n2),(3,n3),(4,n4),(5,n5),(6,n6),(7,n7),(8,n8),(9,n9),(10,n10)]
And then plot them on top of:
x = numpy.linspace(0,15,100)
y = 1
plt.plot(x,y,'g')
Can this be done?
You need to zip:
xvalues = [1,2,3,4,5,6,7,8,9,10]
yvalues = ["n1","n2","n3","n4","n5","n6","n7","n8","n9","n10"]
print(zip(xvalues,yvalues))
Output:
[(1, 'n1'), (2, 'n2'), (3, 'n3'), (4, 'n4'), (5, 'n5'), (6, 'n6'), (7, 'n7'), (8, 'n8'), (9, 'n9'), (10, 'n10')]

List coordinates between a set of coordinates

This should be fairly easy, but I'm getting a headache from trying to figure it out. I want to list all the coordinates between two points. Like so:
1: (1,1)
2: (1,3)
In between: (1,2)
Or
1: (1,1)
2: (5,1)
In between: (2,1), (3,1), (4,1)
It does not need to work with diagonals.
You appear to be a beginning programmer. A general technique I find useful is to do the job yourself, on paper, then look at how you did it and translate that to a program. If you can't see how, break it down into simpler steps until you can.
Depending on how you want to handle the edge cases, this seems to work:
def points_between(p1, p2):
xs = range(p1[0] + 1, p2[0]) or [p1[0]]
ys = range(p1[1] + 1, p2[1]) or [p1[1]]
return [(x,y) for x in xs for y in ys]
print points_between((1,1), (5,1))
# [(2, 1), (3, 1), (4, 1)]
print points_between((5,6), (5,12))
# [(5, 7), (5, 8), (5, 9), (5, 10), (5, 11)]

Categories