How to check when a vector has made one turns in python

Related searches

I am actually working on data that represents a (roughly) noisy circle in a 2d space. i acquire data one point at a time and the goal is know when the points have made a circle.

To do so, i considered each successive points as only one vector that has turned a bit. And to know when one turn has been made (meaning when the circle is formed), I check when both x and y coordinate has change there sign twice (meaning the vector has made 2*0.5 turn = 1 turn). then i wait one more half turn to compensate the starting error. Indeed, depending on where it has started in the quarter of the space it was at first, it may have not do a whole turn.

I don't need to be extremely precise. So this is kind of fine for me, but i wonder if there is another method thas is more efficient and that tells the real number of turns. This could speed up a bit the process as the points arrives quit slowly (avoiding me to wait one more useless half turn)

One important point is that i can only use Numpy.

EDIT : more precision, the distance between each point is NOT regular. At first, the circle starts to be formed slowly and it then speed up. So, at the beginning the points are more dense than at the end. Another thing is that the (0,0) point may even not be contained in the circle. Finally, i said roughly circular shaped because it tends to be ellipsis shaped, but not badly formed, just noisy.

And sorry but i can't provide data, at least for now. I'll tell you if it is possible during the week.

You can monitor the distance of each point to the first point and when this distance reaches a minimum it means the circle has closed. The following shows a plot of point distances to the first point along the circle:

This is the relevant code for the algorithm:

distances = []
points = [next_point()]
while True:  # break below
    points.append(next_point())
    distances.append(np.linalg.norm(points[-1] - points[0]))
    if len(distances) >= 3:
        left = distances[-2] - distances[-3]
        right = distances[-1] - distances[-2]
        if left < 0 and right > 0:  # minimum detected
            break
del points[-1], distances[-1]  # optionally delete the last point in order to leave the circle open

Testing on a data set which varies both the angle difference and the radius the following result is obtained:

This is the full example code:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        # radius = random.uniform(0.95, 1.05)
        radius = 1
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()


def next_point(n=1):
    """n: number of points per group"""
    return sum(next(generator) for __ in range(n)) / n


distances = []
points = [next_point()]
while True:  # break below
    points.append(next_point())
    distances.append(np.linalg.norm(points[-1] - points[0]))
    if len(distances) >= 3:
        left = distances[-2] - distances[-3]
        right = distances[-1] - distances[-2]
        if left < 0 and right > 0:  # minimum detected
            break
del points[-1], distances[-1]  # optionally delete the last point in order to leave the circle open

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10.8, 4.8))
plt.subplots_adjust(wspace=0.11)
ax1.set_title('Data points')
ax1.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax1.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax1.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax1.legend(loc='center')

ax2.set(title='Distance of circle points to first point', xlabel='Point #', ylabel='Distance')
ax2.yaxis.tick_right()
ax2.yaxis.set_label_position('right')
ax2.plot(distances, '-o', ms=4)
ax2.plot(len(distances)-1, distances[-1], '*', ms=10, label='circle closed')
ax2.legend()

plt.show()
Varying radius

In case the radius of data points varies as well it is important to choose a window of sufficient size which will group and average consecutive data points for greater stability. The function next_point can be adjusted by using n=5 for example. The following result is obtained by uncommenting the radius variation in the above code and using a window size of n=5:

Data science with Python: Turn your conditional loops to Numpy , Numpy, short for Numerical Python, is the fundamental package required heavy use of linear algebra operations on a long list/vector/matrix of numbers). in Python, pointer indirection and per-element dynamic type checking. And it turns out one can easily vectorize simple blocks of conditional loops� For example, maybe you want to plot column 1 vs column 2, or you want the integral of data between x = 4 and x = 6, but your vector covers 0 < x < 10. Indexing is the way to do these things. A key point to remember is that in python array/vector indices start at 0. Unlike Matlab, which uses parentheses to index a array, we use brackets in python.

If each new data point is guaranteed to have a greater polar angle than the previous one, i.e. the circle is incrementally formed without any point "stepping-back" in the procedure, then for each pair of consecutive points you can compute the angle between them and then you can stop when the sum reaches two pi. For example:

angle = 0
points = [next(generator)]  # 'generator' produces the data points
while angle < 2*pi:
    points.append(next(generator))
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below 2 pi

Here's an example plot using the above method:

And the example code:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = 0
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = random.uniform(0.95, 1.05)
        yield radius*np.cos(angle), radius*np.sin(angle)
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()

angle = 0
points = [next(generator)]  # 'generator' produces the data points
while angle < 2*pi:
    points.append(next(generator))
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below 2 pi

fig, ax = plt.subplots()
ax.scatter(*np.stack(points, axis=1), s=5)
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot([0, points[ 0][0]], [0, points[ 0][1]], '--s', ms=8, label='First point', color='#2ca02c')
ax.plot([0, points[-1][0]], [0, points[-1][1]], '--*', ms=12, label='Last point', color='#ff7f0e')
ax.legend()

plt.show()

4. NumPy Basics: Arrays and Vectorized Computation, This feature has made Python a language of choice for wrapping legacy One of the key features of NumPy is its N-dimensional array object, or ndarray, which is a large data sets, it is good to know that you have control over the storage type. Turns out this can be computed using argmax , which returns the first index of� Numpy provides a C-API for even faster code execution but it takes away the simplicity of Python programming. This Scipy lecture note shows all the related options you have in this regard. There is an entire open-source, online book on this topic by a French neuroscience researcher. Check it out here.

You can use the first point as an offset which will be subtracted from all the points. This shifts the edge of the circle to the origin. Now imagine a tangent to the circle (at any point, but specifically we will be using the origin), then the circle lies completely on one side of the tangent. The tangent itself spans 180 degrees and if we walk along the circle starting from the origin, always measuring the angle between consecutive vectors, we will have measured 180 degrees in total once we arrive back to the origin (in case the points on the circle are infinitesimally spaced). This allows to compute the accumulated sum of angles and stop when it reaches 180 degrees (= pi). Now since in reality the points have a finite spacing we will miss some fraction of the 180 degrees at the beginning and the end of the circle (w.r.t. the origin). This implies that when we reach 180 degrees we will have collected slightly more points than are necessary in order to close the circle; the OP indicates that this is desired behavior, i.e. the circle must be closed (better some overlap than not closed).

This is the relevant code for the algorithm:

angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next(generator) - offset]
while angle <= pi:
    points.append(next(generator) - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

The following plot shows an example where the radius of each point is exactly the same, only the polar angle varies:

This is the complete example code:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = 1
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()

angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next(generator) - offset]
while angle <= pi:
    points.append(next(generator) - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax.legend()

plt.show()
Varying radius

If however the data points also have varying radius the method suffers from these variations as viewed from the origin. In this case consecutive data points can be grouped together and then using the mean value for greater stability:

This is the code using a grouping of data points for greater stability:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = random.uniform(0.95, 1.05)
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()


def next_point(n=5):
    """n: number of points per group"""
    return sum(next(generator) for __ in range(n)) / n


angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next_point() - offset]
while angle <= pi:
    points.append(next_point() - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax.legend(loc='center')

plt.show()

Vector magnitude & normalization (article), Perhaps you're wondering: “OK, so how do I know what the length of a vector is direction, change its length to 1, turning it into what is called a unit vector. Have you thought that it might have made more sense because it was easier content� In this article you learn to make arrays and vectors in Python. Read data pacakages into Python First we will read the packages into the Python library: # Read packages into Python library: import numpy as np Build the array/vector in Python Next we will build the array/vector in Python: # Build array/vector: x = […]

How to One Hot Encode Sequence Data in Python, After completing this tutorial, you will know: Let's make this concrete with a worked example. The vector will have a length of 2 for the 2 possible integer values. In turn, the 'green' label encoded as a 1 will be represented with a binary vector [0, 1] where the first index is marked with a value of 1. Chapter 1. Vectors, Matrices, and Arrays 1.0 Introduction NumPy is the foundation of the Python machine learning stack. NumPy allows for efficient operations on the data structures often used in … - Selection from Machine Learning with Python Cookbook [Book]

24.1. turtle — Turtle graphics — Python 3.3.7 documentation, Give it the command turtle.right(25), and it rotates in-place 25 degrees clockwise. ScrolledCanvas or TurtleScreen as argument, so the RawTurtle objects know To use multiple turtles on a screen one has to use the object-oriented interface. Return the distance from the turtle to (x,y), the given vector, or the given other� Mathematically, a vector is a tuple of n real numbers where n is an element of the Real (R) number space. Each number n (also called a scalar) represents a dimension. For example, the vector v = (x, y, z) denotes a point in the 3-dimensional space where x, y, and z are all Real numbers. Q So how do we create a vector in Python?

Python: check if key exists in dictionary (6 Ways) Python : Get number of elements in a list, lists of lists or nested list; How to check if a file or directory or link exists in Python ? 6 Ways to check if all values in Numpy Array are zero (in both 1D & 2D arrays) - Python; Python : Convert list of lists or nested list to flat list

Comments
  • Can you give us some sample data ? It is possible to have a more efficient solution if we know how the data looks like (How precise the data is as well as how far apart the points you get are). And do we assume your circle is always centered on (0,0) ?
  • Yep sorry, i added details on my first post. Concerning data, i can't provide them, at least for now, sorry.
  • What is your question? What is the specific problem you need help with? What have you tried so far?
  • Sorry but this is all answered in my first post. If you feel i lack precision, tell me where, i'll add details.
  • Thanks a lot for your answers. The other solution seems more elegant to me but this one is better suited. indeed, my data are quit noisy, essentially at the beginning. So the first solution would result in quit a mess in angle tracking for, say, the first tenth of the points (out of a total of around 1000 points). I'll try to implement this as soon as possible.
  • this is great. This could totally fit my need as your generated data truly ressemble mines. The only thing is that my circles may sometimes not contains the (0,0). It does not seem that this solution would work in that case, isn't it ? Currently, i acquire many circles at the same time, so when one is centered and has made one turn, i find all circle 'coarse' centers (has they always are quit formed) and center them. Then run again the algo. Not efficient but it works.
  • @Hattori Indeed it only works if the circle is centered around the origin. I have added another answer that works also if the circle is non-centered; I've added a separate answer since it uses a different concept.