Array too big for division in matlab but not python - python

The problem I have is array too big in Matlab. The array data comes from audio file. I want to get the impulse response.
I first FFT the original and recorded audio. Then the division of recorded by original. Lastly inverse FFT to get the impulse response. That was what I planned to do but I got stuck at the division part.
Stuck using Matlab, I found a python code that can do it just fine. I rewrite the code into Matlab and the problem is back again. The code is incomplete but it is enough to show the problem.
Hope to get many advice and criticism. Thanks
Planned to do but failed so moved on to the next code
[y_sweep,Fs] = audioread('sweep.wav');
[y_rec,Fs] = audioread('edit_rec_sweep_laptop_1.2.wav');
fft_y1 = abs(fft(y_rec(:,1)));
fft_y2 = abs(fft(y_rec(:,2)));
fft_x = abs(fft(y_sweep));
fft_h1 = fft_y1/fft_x;
% fft_h2 = fft_y2/fft_x;
% fft_h = [fft_h1,fft_h2];
% h1 = ifft(fft1_h);
'Translated' code from python but still failed thus came here
[a,fs] = audioread('sweep.wav'); % sweep
[b,fs] = audioread('rec.wav'); % rec
a = pad(a,fs*50,fs*10);
b = pad(b,fs*50,fs*10);
[m,n] = size(b);
h = zeros(m,n);
for chan = 1:2
b1 = b(:,1);
ffta = abs(fft(a));
fftb = abs(fft(b1));
ffth = fftb / ffta;
end
pad.m function (translated from python but should be correct)
function y = pad(data, t_full, t_pre)
[row_dim,col_dim] = size(data);
t_post = t_full - row_dim - t_pre;
if t_post > 0
if col_dim == 1
y = [zeros(t_pre,1);data;zeros(t_post,1)];
% width = [t_pre,t_post];
else
y1 = [zeros(t_pre,1);data(:,1);zeros(t_post,1)];
y2 = [zeros(t_pre,1);data(:,2);zeros(t_post,1)];
y = [y1,y2];
% width = [[t_pre,t_post],[0,0]];
end
else
if col_dim == 1
y = [zeros(t_pre,1);data(t_full - t_pre:end,1)];
% width = [t_pre,0];
else
y = [zeros(t_pre,1);data(t_full - t_pre:end,1)];
% width = [[t_pre,0],[0,0]];
end
end
end
Error
Error using \
Requested 4800000x4800000 (171661.4GB) array exceeds
maximum array size preference. Creation of arrays
greater than this limit may take a long time and
cause MATLAB to become unresponsive. See array size
limit or preference panel for more information.
Error in impulseresponse (line 13)
ffth = fftb / ffta;

The forward slash is shorthand in MATLAB for mrdivide(). This is for solving systems of linear matrix equations. What I think you want is rdivide which is denoted by ./.
c = a/b is only equivalent to standard division if b is scalar.
c = a./b is element-wise division, where every element of a is divided by the corresponding element of b.
[1 2 3] ./ [2 4 9]
>> ans = [0.5, 0.5, 0.3333]
So the last active line of your "planned to do" code becomes
fft_h1 = fft_y1 ./ fft_x;

Related

Method of generating a string with results from a curve_fit

I have created a class which takes a distribution, and fits it. The method has the option for choosing between a few predefined functions.
As part of printing the class, I print the result of the fit in the form of an equation, where the fit-results and subsequent errors are displayed on the over the figure.
My question is is there a tidy way to handle when a number is negative, such that the string for printing is formed as: "y = mx - c", and not "y = mx + -c".
I developed this with a linear fit, where I simply assess the sign of the constant, and form the string in one of two ways:
def fit_result_string(self, results, errors):
if self.fit_model is utl.linear:
if results[1] > 0:
fit_str = r"y = {:.3}($\pm${:.3})x + {:.3}($\pm${:.3})".format(
results[0],
errors[0],
results[1],
errors[1])
else:
fit_str = r"y = {:.3}($\pm${:.3})x - {:.3}($\pm${:.3})".format(
results[0],
errors[0],
abs(results[1]),
errors[1])
return fit_str
I now want to build this up to also be able to form a string containing the results if the fit model is changed to a 2nd, 3rd, or 4th degree polynomial, while handling the sign of each coefficient.
Is there a better way to do this than using a whole bunch of if-else statements?
Thanks in advance!
Define a function which returns '+' or '-' according to the given number, and call it inside a f-string.
def plus_minus_string(n):
return '+' if n >= 0 else '-'
print(f"y = {m}x {plus_minus_string(c)} {abs(c)}")
Examples:
>>> m = 2
>>> c = 5
>>> print(f"y = {m}x {plus_minus_string(c)} {abs(c)}")
y = 2x + 5
>>> c = -4
>>> print(f"y = {m}x {plus_minus_string(c)} {abs(c)}")
y = 2x - 4
You will need to change it a bit to fit to your code, but it's quite straight-forward I hope.

Optimizing array operations in Python with Numpy

I'm baffled. I just ported my code from Java to Python. Goods news is the Python alternative for the lib I'm using is much quicker. Bad part is that my custom processing code is much slower with the Python alternative I wrote :( I even removed some parts I deemed unnecessary, still much slower. The Java version took about half a second, Python takes 5-6.
rimg1 = imageio.imread('test1.png').astype(np.uint8)
rimg2 = imageio.imread('test2.png').astype(np.uint8)
sum_time = 0
for offset in range(-left, right):
rdest = np.zeros((h, w, 3)).astype(np.uint8)
if offset == 0:
continue
mult = np.uint8(1.0 / (offset * multiplier / frames))
for y in range(h):
for x in range(0, w - backup, 1):
slice_time = time.time()
src = rimg2[y,x] // mult + 1
sum_time += time.time() - slice_time
pix = rimg1[y,x + backup]
w ~= 384 and h ~= 384
src ranges from 0 - 30 usually.
left to right is -5 to 5
How come sum_time takes about a third of my total time?
Edit
With the help of josephjscheidt I made some changes.
mult = np.uint8(1.0 / (offset * multiplier / frames))
multArray = np.floor_divide(rimg2, mult) + 1
for y in range(h):
pixy = rimg1[y]
multy = multArray[y]
for x in range(0, w - backup, 1):
src = multy[y]
slice_time = time.time()
pix = pixy[x + backup]
sum_time += time.time() - slice_time
ox = x
for o in range(src):
if ox < 0:
break
rdest[y,ox] = pix
ox-=1
Using the numpy iterator for the srcArray cuts total time almost in half! The numpy operation itself seems to take negligible time.
Now most of the time taken is in rimg1 lookup
pix = rimg1[x + backup]
and the inner for loop (both taking 50% of time). Is it possible to handle this with numpy operations as well?
Edit
I would figure rewriting it could be of benefit, but somehow the following actually takes a little bit longer:
for x in range(0, w - backup, 1):
slice_time = time.time()
lastox = max(x-multy[y], 0)
rdest[y,lastox:x] = pixy[x + backup]
sum_time += time.time() - slice_time
Edit
slice_time = time.time()
depth = multy[y]
pix = pixy[x + backup]
ox = x
#for o in range(depth):
# if ox < 0:
# break;
#
# rdesty[ox] = pix
# ox-=1
# if I uncomment the above lines, and comment out the following two
# it takes twice as long!
lastox = max(x-multy[y], 0)
rdesty[lastox:x] = pixy[x + backup]
sum_time += time.time() - slice_time
The python interpreter is strange..
Time taken is now 2.5 seconds for sum_time. In comparison, Java does it in 60ms
For loops are notoriously slow with numpy arrays, and you have a three-layer for loop here. The underlying concept with numpy arrays is to perform operations on the entire array at once, rather than trying to iterate over them.
Although I can't entirely interpret your code, because most of the variables are undefined in the code chunk you provided, I'm fairly confident you can refactor here and vectorize your commands to remove the loops. For instance, if you redefine offset as a one-dimensional array, then you can calculate all values of mult at once without having to invoke a for loop: mult will become a one-dimensional array holding the correct values. We can avoid dividing by zero using the out argument (setting the default output to the offset array) and where argument (performing the calculation only where offset doesn't equal zero):
mult = np.uint8(np.divide(1.0, (offset * multiplier / frames),
out = offset, where = (offset != 0))
Then, to use the mult array on the rimg2 row by row, you can use a broadcasting trick (here, I'm assuming you want to add one to each element in rimg2):
src = np.floor_divide(rimg2, mult[:,None], out = rimg2, where = (mult != 0)) + 1
I found this article extremely helpful when learning how to effectively work with numpy arrays:
https://realpython.com/numpy-array-programming/
Since you are working with images, you may want to especially pay attention to the section on image feature extraction and stride_tricks. Anyway, I hope this helps you get started.

IDL WHERE in python?

I am having trouble translating an old IDL script to python- my issue lies in understanding exactly how to interpret IDL's "WHERE" function.
Here is my code:
FUNCTION noise,day,y
N = N_ELEMENTS(y)
valid = WHERE(ABS(day[0:N-3]-day[2:N-1]) LT 20,cc)
IF cc LT 2 THEN RETURN,[-9.99,-9.99,-9.99,-9.99]
y_int = (y[0:N-3] * (day[2:N-1] - day[1:N-2]) + y[2:N-1] * (day[1:N-2] - day[0:N-3]))/ (day[2:N-1] - day[0:N-3])
dif = ABS(y_int - y[1:N-2])
difR = ABS(y_int/y[1:N-2] - 1.)
dif = dif [valid]
difR= difR[valid]
; Remove 5% of higher values
Nv = LONG(cc*0.95)
s = SORT(dif) & s = s[0:Nv-1]
noise5 = SQRT(TOTAL(dif[s]^2)/(Nv-1)) ; Absolu Noise minus 5% of higher values
noise = SQRT(TOTAL(dif^2)/(cc-1)) ; Absolu Noise
s = SORT(difR) & s = s[0:Nv-1]
noiseR5 = SQRT(TOTAL(difR[s]^2)/(Nv-1)) ; Relative Noise minus 5% of higher values
noiseR = SQRT(TOTAL(difR^2)/(cc-1)) ; Relative Noise
RETURN,[noise5,noiseR5*100.,noise,noiseR*100.]
END
Can anyone help me understand the python equivalent? TY.
I would translate:
valid = WHERE(ABS(day[0:N-3]-day[2:N-1]) LT 20,cc)
as:
valid = (numpy.abs(day[0:-2] - day[2:]) < 20).nonzero()

Map -1, 0, 1 to 1000, 1500, 2000

This is my first time making an algorithm for any of my projects.
I have an Xbox controller I will be using in a Python script using Pygame to read the output from the controller. Pygame outputs 0 when centered, -1 when full left, and 1 when full right.
For my application I need to translate this to values between 1000 and 2000 where 1000 is -1, 1500 is 0, and 2000 is 1.
Not asking necessarily for an answer, just some help with how to go about making an algorithm for myself.
If these are the only values possible, then you can create a dict to map Pygame outputs to your values.
positionMap = {-1:1000,0:1500,1:2000}
newVal = positionMap[oldVal]
But, if intermediate values are also possible then use this equation:
newVal = oldVal*500 + 1500
Your function can be of the form f(x) = ax^2 + bx + c. Put your transformations in a system and solve it:
a(-1)^2 + b(-1) + c = 1000
a*0^2 + b*0 + c = 1500
a*1^2 + b*1 + c = 2000
a - b + c = 1000
c = 1500
a + b + c = 2000
a - b = -500
a + b = 500
=> 2a = 0 => a = 0
=> b = 500
So you can use the function f(x) = 500x + 1500.
f(-1) = 1000
f(0) = 1500
f(1) = 2000
f(0.3) = 1650
There are many ways to do what you're asking. The simplest way is a linear mapping using the two point form of a line. You probably learned this in algebra but you might have forgotten it so here's a refresher: http://www.mathsisfun.com/algebra/line-equation-2points.html.
In your case, the x values are what you're given (-1 .. 1) and the y values are what you want (1000..2000).
Of course if you'd like to change the feel of the controller, you might choose not to use a linear function. You might want something that slows down as you approach the limits of the controller for example.
def mapInput(value):
minInput = -1
maxInput = 1
minOutput = 1000
maxOutput = 2000
return (value - minInput)*(maxOutput-minOutput)/(maxInput-minInput) + minOutput
If those values are never going to change, then you could save a few processor cycles and just do:
def mapInput(value):
return value * 500 + 1500
If you are new to Python too, then might like to use an if/elif/else statement as that might be more readable for you:
if in_value == -1:
out_value = 1000
elif in_value == 0:
out_value = 1500
else:
out_value = 2000
The code could be wrapped in a function or used in-line.

Python interpolation error

I am trying to use the interpolation method in python (not the built-in one) to get the root of a function given an interval.
I have done the following and don't know where I am going wrong, I have done it with bisection and I though the only difference would be the test point.
x1 and x2 are the two ends of the interval, f is the function and epsilon is the tolerance
def interpolation (x1,x2,f,epsilon):
i = 1
n = 100
while i<n:
m = (f(x2)- f(x1))/(x2-x1)
b = f(x2) - m*(x2)
p = b
print (i,p,f(p))
if f(p) == 0 or b< epsilon:
print ('The root is at ',p,'after',i,'iterations')
break
i+= 1
if f(x1)*f(p) > 0: #Equal signs
x1 = p
else:
x2 = p
Running this with f = sin(x^2) simply returns 100 iterations oscillating as follows:
code
(80, 1.3266674970489443, 0.98214554271216425)
(81, 1.4900968376899661, 0.79633049173817871)
(82, 1.3266674970489443, 0.98214554271216425)
(83, 1.4900968376899661, 0.79633049173817871)
It looks like you are trying to solve this using the secant method. The interpolation method requires three initial values.
I am not quite sure the direction you were going with your code, but I was able to adjust it a bit as following:
i = 1
n = 100
while i<n:
print x1, x2
m = (f(x2)- f(x1))/(x2-x1)
b = f(x2) - m*(x2)
p = -b/m #root for this line
# are we close enough?
if abs(f(p)) < epsilon:
print ('The root is at ',p,'after',i,'iterations')
break
i+= 1
x1 = x2
x2 = p
It solved it in 4 iterations based on my starting positions of 1,2:
1 2
2 1.52648748495
1.52648748495 1.75820676726
1.75820676726 1.7754676477
('The root is at ', 1.7724406948343991, 'after', 4, 'iterations')
In case what you actually want is to solve the problem (instead of developing a solution for exercise), I recommend you to use a ready-made module.
My first choice would be scipy.optimize.bisect() (docs)
This module has other methods, too, like Newton-Raphson, etc.

Categories

Resources