Related
How do I implement the below in a JAX-compatable way (e.g., using jax.numpy)?
def actions(state: tuple[int, ...]) -> list[tuple[int, ...]]:
l = []
iterables = [range(1, i+1) for i in state]
ns = list(range(len(iterables)))
for i, iterable in enumerate(iterables):
for value in iterable:
action = tuple(value if n == i else 0 for n in ns)
l.append(action)
return l
>>> state = (3, 1, 2)
>>> actions(state)
[(1, 0, 0), (2, 0, 0), (3, 0, 0), (0, 1, 0), (0, 0, 1), (0, 0, 2)]
Jax, like numpy, cannot efficiently operate on Python container types like lists and tuples, so there's not really any JAX-compatible way to create a function with the exact signature you specify above.
But if you're alright with the return value being a two-dimensional array, you could do something like this, based on jnp.vstack:
from typing import Tuple
import jax.numpy as jnp
from jax import jit, partial
#partial(jit, static_argnums=0)
def actions(state: Tuple[int, ...]) -> jnp.ndarray:
return jnp.vstack([
jnp.zeros((val, len(state)), int).at[:, i].set(jnp.arange(1, val + 1))
for i, val in enumerate(state)])
>>> state = (3, 1, 2)
>>> actions(state)
DeviceArray([[1, 0, 0],
[2, 0, 0],
[3, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 0, 2]], dtype=int32)
Note that because the size of the output array depends on the content of state, state must be a static quantity, so a tuple is a good option for the input.
So i'm having trouble to solve the following problem:
given an array size, lets say for the ease of the question size =20
it is filled with zeros as follows
arr = [0]*20 ==> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
we have couple of constant sample sizes, such as 4,3,2
SampleA=4, SampleB=3, SampleC= 2
i need to have the permutations/variations of how to allocate the list.
i can put each sample in different place/index
for example, sampleA= 4 i can put it in indexes of 0:3, or 1:4... 15:19..
(as you can see, there are quite a lot of possibilities)
the thing gets complicated once it get more crowded, for example:
3+2+3 +4
[0, x, x, x, 0, 0, x x, 0, x, x, x, 0, 0, 0, 0, x, x,x, x]
what i basically need, is to find all the possibilities to allocate samples,
i get a dictionary:
key = sample size of indexes, and the
value=many times it repeats.
for the upper example: {3:2,2:1,4:1}
and i would like the function to return a list of indexes !=0
for this example:
[0, x, x, x, 0, 0, x x, 0, x, x, x, 0, 0, 0, 0, x, x,x, x]
the function will return:
list_ind = [0,5,6,9,13,14,15,16]
so i asked a colleague to help, and we came into a solution:
i put an example of:
4:2, 3:1, 2:1
or verbally:
two times 4, one time 3, one time 2
the code below:
*if someone can optimize, will be great
size_of_wagon = 20
dct = {4:2,3:1,2:1}
l = [item for sublist in [[k] * v for (k, v) in dct.items()] for item in sublist]
def gen_func(lstOfSamples, length, shift):
try:
# print(f'lstOfSamples={lstOfSamples}')
sample = lstOfSamples[0] # Take first sample
for i in range(length - sample):
for x in gen_func(lstOfSamples[1:], length - (sample + i), shift + sample + i):
yield [(shift + i, sample)] + x
except:
yield []
g = list(gen_func(l, size_of_wagon, 0))
for i in g:
print(i)
print(len(g))
Given a list in Python containing 8 x, y coordinate values (all positive) of 4 points as [x1, x2, x3, x4, y1, y2, y3, y4] ((xi, yi) are x and y coordinates of ith point ),
How can I sort it such that new list [a1, a2, a3, a4, b1, b2, b3, b4] is such that coordinates (ai, bi) of 1 2 3 4 are clockwise in order with 1 closest to origin of xy plane, i.e. something like
2--------3
| |
| |
| |
1--------4
Points will roughly form a parallelogram.
Currently, I am thinking of finding point with least value of (x+y) as 1, then 2 by the point with least x in remaining coordinates, 3 by largest value of (x + y) and 4 as the remaining point
You should use a list of 2-item tuples as your data structure to represent a variable number of coordinates in a meaningful way.
from functools import reduce
import operator
import math
coords = [(0, 1), (1, 0), (1, 1), (0, 0)]
center = tuple(map(operator.truediv, reduce(lambda x, y: map(operator.add, x, y), coords), [len(coords)] * 2))
print(sorted(coords, key=lambda coord: (-135 - math.degrees(math.atan2(*tuple(map(operator.sub, coord, center))[::-1]))) % 360))
This outputs:
[(0, 0), (0, 1), (1, 1), (1, 0)]
import math
def centeroidpython(data):
x, y = zip(*data)
l = len(x)
return sum(x) / l, sum(y) / l
xy = [405952.0, 408139.0, 407978.0, 405978.0, 6754659.0, 6752257.0, 6754740.0, 6752378.0]
xy_pairs = list(zip(xy[:int(len(xy)/2)], xy[int(len(xy)/2):]))
centroid_x, centroid_y = centeroidpython(xy_pairs)
xy_sorted = sorted(xy_pairs, key = lambda x: math.atan2((x[1]-centroid_y),(x[0]-centroid_x)))
xy_sorted_x_first_then_y = [coord for pair in list(zip(*xy_sorted)) for coord in pair]
# P4=8,10 P1=3,5 P2=8,5 P3=3,10
points=[8,3,8,3,10,5,5,10]
k=0
#we know these numbers are extreme and data won't be bigger than these
xmin=1000
xmax=-1000
ymin=1000
ymax=-1000
#finding min and max values of x and y
for i in points:
if k<4:
if (xmin>i): xmin=i
if (xmax<i): xmax=i
else:
if (ymin>i): ymin=i
if (ymax<i): ymax=i
k +=1
sortedlist=[xmin,xmin,xmax,xmax,ymin,ymax,ymax,ymin]
print(sortedlist)
output:[3, 3, 8, 8, 5, 10, 10, 5]
for other regions you need to change sortedlist line. if center is inside the box then it will require more condition controlling
What we want to sort by is the angle from the start coordinate. I've used numpy here to interpret each vector from the starting coordinate as a complex number, for which there is an easy way of computing the angle (counterclockwise along the unit sphere)
def angle_with_start(coord, start):
vec = coord - start
return np.angle(np.complex(vec[0], vec[1]))
Full code:
import itertools
import numpy as np
def angle_with_start(coord, start):
vec = coord - start
return np.angle(np.complex(vec[0], vec[1]))
def sort_clockwise(points):
# convert into a coordinate system
# (1, 1, 1, 2) -> (1, 1), (1, 2)
coords = [np.array([points[i], points[i+4]]) for i in range(len(points) // 2)]
# find the point closest to the origin,
# this becomes our starting point
coords = sorted(coords, key=lambda coord: np.linalg.norm(coord))
start = coords[0]
rest = coords[1:]
# sort the remaining coordinates by angle
# with reverse=True because we want to sort by clockwise angle
rest = sorted(rest, key=lambda coord: angle_with_start(coord, start), reverse=True)
# our first coordinate should be our starting point
rest.insert(0, start)
# convert into the proper coordinate format
# (1, 1), (1, 2) -> (1, 1, 1, 2)
return list(itertools.chain.from_iterable(zip(*rest)))
Behavior on some sample inputs:
In [1]: a
Out[1]: [1, 1, 2, 2, 1, 2, 1, 2]
In [2]: sort_clockwise(a)
Out[2]: [1, 1, 2, 2, 1, 2, 2, 1]
In [3]: b
Out[3]: [1, 2, 0, 2, 1, 2, 3, 1]
In [4]: sort_clockwise(b)
Out[4]: [1, 0, 2, 2, 1, 3, 2, 1]
Based on BERA's answer but as a class:
code
import math
def class Sorter:
#staticmethod
def centerXY(xylist):
x, y = zip(*xylist)
l = len(x)
return sum(x) / l, sum(y) / l
#staticmethod
def sortPoints(xylist):
cx, cy = Sorter.centerXY(xylist)
xy_sorted = sorted(xylist, key = lambda x: math.atan2((x[1]-cy),(x[0]-cx)))
return xy_sorted
test
def test_SortPoints():
points=[(0,0),(0,1),(1,1),(1,0)]
center=Sorter.centerXY(points)
assert center==(0.5,0.5)
sortedPoints=Sorter.sortPoints(points)
assert sortedPoints==[(0, 0), (1, 0), (1, 1), (0, 1)]
As suggested by IgnacioVazquez-Abrams, we can also do sorting according to atan2 angles:
Code:
import math
import copy
import matplotlib.pyplot as plt
a = [2, 4, 5, 1, 0.5, 4, 0, 4]
print(a)
def clock(a):
angles = []
(x0, y0) = ((a[0]+a[1]+a[2]+a[3])/4, (a[4]+ a[5] + a[6] + a[7])/4) # centroid
for j in range(4):
(dx, dy) = (a[j] - x0, a[j+4] - y0)
angles.append(math.degrees(math.atan2(float(dy), float(dx))))
for k in range(4):
angles.append(angles[k] + 800)
# print(angles)
z = [copy.copy(x) for (y,x) in sorted(zip(angles,a), key=lambda pair: pair[0])]
print("z is: ", z)
plt.scatter(a[:4], a[4:8])
plt.show()
clock(a)
Output is :
[2, 4, 5, 1, 0.5, 4, 0, 4]
[-121.60750224624891, 61.92751306414704, -46.73570458892839, 136.8476102659946, 678.3924977537511, 861.9275130641471, 753.2642954110717, 936.8476102659946]
z is: [2, 5, 4, 1, 0.5, 0, 4, 4]
Try this line of code
def sort_clockwise(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
I am investigating whether storing points in a numpy array helps me search for points, and I have several questions about it.
I have a Point class that represents a 3-dimensional point.
class Point( object ):
def __init__( self, x, y, z ):
self.x = x
self.y = y
self.z = z
def __repr__( self ):
return "<Point (%r, %r, %r)>" % ( self.x, self.y, self.z )
I build a list of Point objects. Notice that the coordinates (1, 2, 3) deliberately occurs twice; that is what I am going to search for.
>>> points = [Point(1, 2, 3), Point(4, 5, 6), Point(1, 2, 3), Point(7, 8, 9)]
I store the Point objects in a numpy array.
>>> import numpy
>>> npoints = numpy.array( points )
>>> npoints
array([<Point (1, 2, 3)>, <Point (4, 5, 6)>, <Point (1, 2, 3)>,
<Point (7, 8, 9)>], dtype=object)
I search for all points with coordinates (1, 2, 3) in the following manner.
>>> numpy.where( npoints == Point(1, 2, 3) )
>>> (array([], dtype=int64),)
But, the result is not useful. So, that does not seem to be the correct way to do it. Is numpy.where the thing to use? Is there another way to express the condition for numpy.where that would be successful?
The next thing I try is to store just the coordinates of the points in a numpy array.
>>> npoints = numpy.array( [(p.x, p.y, p.z) for p in points ])
>>> npoints
array([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[7, 8, 9]])
I search for all points with coordinates (1,2,3) in the following manner.
>>> numpy.where( npoints == [1,2,3] )
(array([0, 0, 0, 2, 2, 2]), array([0, 1, 2, 0, 1, 2]))
The result is, at least, something I can deal with. The array of row indexes in the first return value, array([0, 0, 0, 2, 2, 2]), does indeed tell me that the coordinates I am searching for are in rows 0 and 2 of npoints. I could get away with doing something like the following.
>>> rows, cols = numpy.where( npoints == [1,2,3] )
>>> rows
array([0, 0, 0, 2, 2, 2])
>>> cols
array([0, 1, 2, 0, 1, 2])
>>> foundRows = set( rows )
>>> foundRows
set([0, 2])
>>> for r in foundRows:
... # Do something with npoints[r]
However, I feel that I am not really using numpy.where appropriately, and that I am just getting lucky in this particular situation.
What is the appropriate way to find all occurrences of a n-dimensional point (i.e., a row with particular values) in a numpy array?
Preserving the order of the array is essential.
You can create a “rich comparison” method object.__eq__(self, other) inside your Point class to be able to use == among Point objects:
class Point( object ):
def __init__( self, x, y, z ):
self.x = x
self.y = y
self.z = z
def __repr__( self ):
return "<Point (%r, %r, %r)>" % ( self.x, self.y, self.z )
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.z == other.z
import numpy
points = [Point(1, 2, 3), Point(4, 5, 6), Point(1, 2, 3), Point(7, 8, 9)]
npoints = numpy.array( points )
found = numpy.where(npoints == Point(1, 2, 3))
print(found) # => (array([0, 2]),)
I have a dictionary that looks like this:
1: ['4026', '4024', '1940', '2912', '2916], 2: ['3139', '2464'], 3:['212']...
For a few hundred keys, I'd like to plot them with the key as y for its set of x values, I tried this bit of code which gives the error underneath:
for rank, structs in b.iteritems():
y = b.keys()
x = b.values()
ax.plot(x, y, 'ro')
plt.show()
ValueError: setting an array element with a sequence
I'm at a bit of a loss on how to proceed so any help would be greatly appreciated!
You need to construct your list of Xs and Ys manually:
In [258]: b={1: ['4026', '4024', '1940', '2912', '2916'], 2: ['3139', '2464'], 3:['212']}
In [259]: xs, ys=zip(*((int(x), k) for k in b for x in b[k]))
In [260]: xs, ys
Out[260]: ((4026, 4024, 1940, 2912, 2916, 3139, 2464, 212), (1, 1, 1, 1, 1, 2, 2, 3))
In [261]: plt.plot(xs, ys, 'ro')
...: plt.show()
resulting:
1) Repeat your x values
plot expects a list of x values and a list of y values which have to have the same length. That's why you have to repeat the rank value several times. itertools.repeat() can do that for you.
2) change your iterator
iteritems() already returns a tuple (key,value). You don't have to use keys() and items().
Here's the code:
import itertools
for rank, structs in b.iteritems():
x = list(itertools.repeat(rank, len(structs)))
plt.plot(x,structs,'ro')
3) combine the plots
Using your code, you'd produce one plot per item in the dictionary. I guess you rather want to plot them within a single graph. If so, change your code accrodingly:
import itertools
x = []
y = []
for rank, structs in b.iteritems():
x.extend(list(itertools.repeat(rank, len(structs))))
y.extend(structs)
plt.plot(x,y,'ro')
4) example
Here's an example using your data:
import itertools
import matplotlib.pyplot as plt
d = {1: ['4026', '4024', '1940', '2912', '2916'], 2: ['3139', '2464'], 3:['212']}
x= []
y= []
for k, v in d.iteritems():
x.extend(list(itertools.repeat(k, len(v))))
y.extend(v)
plt.xlim(0,5)
plt.plot(x,y,'ro')
This is because you mismatched your data entries.
Currently you have
1: ['4026', '4024', '1940', '2912', '2916']
2: ['3139', '2464'],
...
hence
x = [1,2,...]
y = [['4026', '4024', '1940', '2912', '2916'],['3139', '2464'],...
when you really need
x = [1, 1, 1, 1, 1, 2, 2, ...]
y = ['4026', '4024', '1940', '2912', '2916', '3139', '2464',...]
Try
for rank, structs in b.iteritems():
# This matches each value for a given key with the appropriate # of copies of the
# value and flattens the list
# Produces x = [1, 1, 1, 1, 1, 2, 2, ...]
x = [key for (key,values) in b.items() for _ in xrange(len(values))]
# Flatten keys list
# Produces y = ['4026', '4024', '1940', '2912', '2916, '3139', '2464',...]
y = [val for subl in b.values() for val in subl]
ax.plot(x, y, 'ro')
plt.show()