Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences between Matlab and Numpy and Python's `round` function

Simplified question

Can I make Numpy agree with Matlab and Python's round?

Matlab 2013a:

>> round(-0.5)
ans =
    -1

Python (using a Numpy array, or just a scalar, same result):

>>> import numpy
>>> round(numpy.array(-0.5))
-1.0

Numpy, the odd one out:

>>> import numpy
>>> numpy.round(numpy.array(-0.5))
-0

Is this difference in round platform dependent?

Original question

Matlab comes with a file "handel.mat" containing some audio data:

>> which handel.mat
C:\Program Files\MATLAB\R2013a\toolbox\matlab\audiovideo\handel.mat
>> load handel
>> soundsc(y) % play the short audio clip

I want to work with this data in Python so I use scipy.io.loadmat [1]. Specifically, I want to scale the audio's values to span the entire range of 16-bit signed integer, i.e., the smallest value of the audio signal gets mapped to -2^15 and the largest one to 2^15-1. I was surprised when doing this in Matlab gave me different results than Python:

Matlab:

>> load handel
>> int16(round(interp1([min(y), max(y)], [-2^15, 2^15-1], y(1:10))))
ans =
     -1              %%% <-- Different from Python
   -253
  -3074
  -1277
    252
   1560
    772
  -1025
  -1277
  -3074

Python:

In [1]: import numpy as np

In [2]: import scipy.io as io

In [3]: mat = io.loadmat('handel.mat')

In [4]: np.int16(np.round(np.interp(mat['y'][:10], [mat['y'].min(), mat['y'].max()], [-2.0**15, 2.0**15-1.0])))
Out[4]:
array([[    0],      ### <-- Different from Matlab
       [ -253],
       [-3074],
       [-1277],
       [  252],
       [ 1560],
       [  772],
       [-1025],
       [-1277],
       [-3074]], dtype=int16)

There are actually 1231 samples (out of 73113 total) where the Python and Matlab differ. I think I'm being careful with my types, but really, there's very little error surface for type bugs to creep in here: loadmat should infer the types from the MAT file, and int16 can't differ that much between the two systems.

Added The first element of the output of the interp/interp1d commands are both -0.5 (printing it to the 100th decimal place in both Python and Matlab confirms this), but rounding in Numpy (np.round) yields 0, while Matlab rounds it to -1. Is this a matter of Matlab rounding semantics? Furthermore Python's built-in non-Numpy round for -0.5 gives me -1! Whence this difference between Numpy's and Python's round functions? And will Python's round always match Matlab's?

Windows64, Matlab 8.1 (2013a), Python 2.7.4.

[1] http://docs.scipy.org/doc/scipy/reference/generated/scipy.io.loadmat.html

like image 909
Ahmed Fasih Avatar asked Oct 21 '22 00:10

Ahmed Fasih


2 Answers

I think you can take advantage of numpy.vectorize to create a custom numpy round function using standard python round function:

>>> import numpy as np
>>> myround = np.vectorize(lambda x: round(x))
>>> a = np.array([-0.5, 0.5, -1.5, 1.5, -2.5, 2.5, 3.5, -3.5])
>>> print myround(a)
 [-1.  1. -2.  2. -3.  3.  4. -4.]

Which is the same result that shows Matlab:

>> a = [-1.  1. -2.  2. -3.  3.  4. -4.];
>> round(a)
 ans =
    -1     1    -2     2    -3     3     4    -4
like image 99
jabaldonedo Avatar answered Oct 30 '22 18:10

jabaldonedo


numpy.round, also known as numpy.around, rounds to the nearest even value for half-integers. This is not platform dependent, but a purposeful implementation detail.

If you wish to have a quick round without using Python, see this answer.

The summary is that there's a platform dependant hack to set rounding using fesetround via ctypes. From the post:

import numpy as np
import ctypes
FE_TONEAREST = 0x0000
FE_DOWNWARD = 0x0400
FE_UPWARD = 0x0800
FE_TOWARDZERO = 0x0c00
libc = ctypes.CDLL('libc.dylib')

v = 1. / (1<<23)
print repr(np.float32(1+v) - np.float32(v/2)) # prints 1.0
libc.fesetround(FE_UPWARD)
print repr(np.float32(1+v) - np.float32(v/2)) # prints 1.0000002
like image 25
Veedrac Avatar answered Oct 30 '22 18:10

Veedrac