chasep255
chasep255

Reputation: 12185

Numpy converting range of angles from (-Pi, Pi) to (0, 2*Pi)

This seems like it would be very straight forward but I can't seem to figure out how to map angles between -Pi and Pi to the range 0 to 2Pi. I tried using np.select but it freezes my program for some reason. I need the angles in this range since they will be used as training data for a neural net which can not output negative numbers.

audio = wav.read('/home/chase/Desktop/ge.wav')[1].astype(np.float32)
audio = np.mean(audio, 1)
audio /= np.max(np.abs(audio))
audio = np.array([np.fft.rfft(audio[i:i + FRAME_SIZE]) for i in range(0, len(audio) - len(audio) % FRAME_SIZE, FRAME_SIZE)])
audio /= FRAME_SIZE
audio_mag = np.abs(audio)
audio_phase = np.angle(audio)

#this line freezes the program
audio_phase = np.select(audio_phase < 0 , 2 * np.pi + audio_phase, audio_phase)

I need the audio

Upvotes: 25

Views: 52924

Answers (5)

&#197;ustin
&#197;ustin

Reputation: 81

I've also found this worked for wrapping and angle between [0-2pi] by using part of Neapolitan's answer above.

import numpy as np
def wrap(angle):
    tmp = np.arctan2(np.sin(angle), np.cos(angle))
    return float(np.where(tmp<0 , 2*np.pi+tmp, tmp))

Some of the other methods had issues when the angles went above double of 2 pi.

Upvotes: 0

Mikkel
Mikkel

Reputation: 81

Angles between -Pi and +Pi:

>>> import numpy as np
>>> angles = np.linspace(-np.pi, np.pi, 10)

Shift to 0 to 2*Pi:

>>> angles_2pi = np.mod(angles, 2*np.pi)
>>> angles_2pi
array([3.14159265, 3.83972435, 4.53785606, 5.23598776, 5.93411946,
   0.34906585, 1.04719755, 1.74532925, 2.44346095, 3.14159265])

Upvotes: 8

Neapolitan
Neapolitan

Reputation: 2163

Here are several different methods complete with timing:

In [1]: import numpy as np; from numpy import linspace, pi

In [2]: N=10000

In [3]: %timeit x=linspace(-pi, pi, N); np.where(x<0 , 2*pi+x, x)
10000 loops, best of 3: 79.1 µs per loop

In [4]: %timeit x=linspace(-pi, pi, N); np.select(x<0 , 2*pi+x, x)
1 loops, best of 3: 354 ms per loop

In [5]: %timeit x=linspace(-pi, pi, N); x[x<0] += 2*pi
10000 loops, best of 3: 82.5 µs per loop

In [6]: %timeit x=linspace(-pi, pi, N); (x + 2*pi)*(x<0) + x*(x>=0)
10000 loops, best of 3: 149 µs per loop

In [7]: %timeit x=linspace(-pi, pi, N); (x + 2*pi)%(2*pi)
10000 loops, best of 3: 192 µs per loop

I find x[x<0] += 2*pi the most readable, but where(x<0, x+2*pi, x) is slightly faster. The select form is by far the slowest.

For comparison, here is the plain linspace function on the same machine:

In [8]: %timeit x=linspace(-pi, pi, N)
10000 loops, best of 3: 35.9 µs per loop

Given that the program is failing on the select and not the far more expensive fft function, you may be running into a low memory condition, with the array paging to disk (both x<0 and 2*pi+x generate arrays, plus you have the original x and the return value created by select). If this is the case, then you may be better off correcting phase frame by frame.

Upvotes: 20

ssm
ssm

Reputation: 5383

Let us say you have these angles:

>>> angles = np.linspace(-np.pi, np.pi, 10)
>>> angles
array([-3.14159265, -2.44346095, -1.74532925, -1.04719755, -0.34906585,
    0.34906585,  1.04719755,  1.74532925,  2.44346095,  3.14159265])

There are two possible ways of doing this ...

  1. Use numpy expressions for finding the negative values ...

The ones less than zero should be converted to the right value. Something like this:

>>> (2*np.pi + angles) * (angles < 0) + angles*(angles > 0)
array([ 3.14159265,  3.83972435,  4.53785606,  5.23598776,  5.93411946,
    0.34906585,  1.04719755,  1.74532925,  2.44346095,  3.14159265])

Remember that in numpy, you can do logical tests ... angles < 0 is a boolean array. However, 1*(angles < 0) is a numeric array, where True values are mapped to 1 and False values are mapped to 0. You can combine the two concepts to get your answer.

  1. You can simply recognize that this is a mathematical expression you are trying to solve:

So for that, simply add 2*np.pi to everything and take the mod. This is exactly like finding the "units place", or converting a number into an octal number etc ...

>>> (angles + 2 * np.pi) % (2 * np.pi)
array([ 3.14159265,  3.83972435,  4.53785606,  5.23598776,  5.93411946,
    0.34906585,  1.04719755,  1.74532925,  2.44346095,  3.14159265])

I hope this helps.

Upvotes: 15

Adolfo Correa
Adolfo Correa

Reputation: 823

You can try use the function map with lambda

>>>angles = np.linspace(-np.pi, np.pi, 10)
array([-3.14159265, -2.44346095, -1.74532925, -1.04719755, -0.34906585,
        0.34906585,  1.04719755,  1.74532925,  2.44346095,  3.14159265])

>>>map(lambda x: x if x > 0 else x + 2*np.pi, angles)

[3.1415926535897931, 3.839724354387525, 4.5378560551852569, 5.2359877559829879, 5.9341194567807207, 0.34906585039886595, 1.0471975511965974, 1.7453292519943293, 2.4434609527920612, 3.1415926535897931]

Upvotes: -1

Related Questions