MATLAB® and NumPy/SciPy have a lot in common. But there are manydifferences. NumPy and SciPy were created to do numerical and scientificcomputing in the most natural way with Python, not to be MATLAB® clones.This page is intended to be a place to collect wisdom about thedifferences, mostly for the purpose of helping proficient MATLAB® usersbecome proficient NumPy and SciPy users.
| In MATLAB®, the basic data type is a multidimensional array ofdouble precision floating point numbers. Most expressions take sucharrays and return such arrays. Operations on the 2-D instances ofthese arrays are designed to act more or less like matrix operationsin linear algebra. | In NumPy the basic type is a multidimensionalarray. Operationson these arrays in all dimensionalities including 2D are element-wiseoperations. One needs to use specific functions for linear algebra(though for matrix multiplication, one can use the@ operatorin python 3.5 and above). |
| MATLAB® uses 1 (one) based indexing. The initial element of asequence is found using a(1).See note INDEXING | Python uses 0 (zero) based indexing. The initial element of asequence is found using a[0]. |
| MATLAB®’s scripting language was created for doing linear algebra.The syntax for basic matrix operations is nice and clean, but the APIfor adding GUIs and making full-fledged applications is more or lessan afterthought. | NumPy is based on Python, which was designed from the outset to bean excellent general-purpose programming language. While Matlab’ssyntax for some array manipulations is more compact thanNumPy’s, NumPy (by virtue of being an add-on to Python) can do manythings that Matlab just cannot, for instance dealing properly withstacks of matrices. |
| In MATLAB®, arrays have pass-by-value semantics, with a lazycopy-on-write scheme to prevent actually creating copies until theyare actually needed. Slice operations copy parts of the array. | In NumPy arrays have pass-by-reference semantics. Slice operationsare views into an array. |
Historically, NumPy has provided a special matrix type,np.matrix, whichis a subclass of ndarray which makes binary operations linear algebraoperations. You may see it used in some existing code instead ofnp.array.So, which one to use?
Use arrays.
Until Python 3.5 the only disadvantage of using the array type was that youhad to usedot instead of* to multiply (reduce) two tensors(scalar product, matrix vector multiplication etc.). Since Python 3.5 youcan use the matrix multiplication@ operator.
Given the above, we intend to deprecatematrix eventually.
NumPy contains both anarray class and amatrix class. Thearray class is intended to be a general-purpose n-dimensional arrayfor many kinds of numerical computing, whilematrix is intended tofacilitate linear algebra computations specifically. In practice thereare only a handful of key differences between the two.
* and@, functionsdot(), andmultiply():array,``*`` means element-wise multiplication, while``@`` means matrix multiplication; they have associated functionsmultiply() anddot(). (Before python 3.5,@ did not existand one had to usedot() for matrix multiplication).matrix,``*`` means matrix multiplication, and forelement-wise multiplication one has to use themultiply() function.array, thevector shapes 1xN, Nx1, and N are all differentthings. Operations likeA[:,1] return a one-dimensional array ofshape N, not a two-dimensional array of shape Nx1. Transpose on aone-dimensionalarray does nothing.matrix,one-dimensional arrays are always upconverted to 1xNor Nx1 matrices (row or column vectors).A[:,1] returns atwo-dimensional matrix of shape Nx1.array objectscan have number of dimensions > 2;matrix objectsalways have exactly two dimensions.arrayhas a .T attribute, which returns the transpose ofthe data.matrixalso has .H, .I, and .A attributes, which returnthe conjugate transpose, inverse, andasarray() of the matrix,respectively.array constructortakes (nested) Python sequences asinitializers. As in,array([[1,2,3],[4,5,6]]).matrix constructor additionallytakes a convenientstring initializer. As inmatrix("[1 2 3; 4 5 6]").There are pros and cons to using both:
array:) Element-wise multiplication is easy:A*B.:( You have to remember that matrix multiplication has its ownoperator,@.:) You can treat one-dimensional arrays aseither row or columnvectors.A@v treatsv as a column vector, whilev@A treatsv as a row vector. This can save you having totype a lot of transposes.:)array is the “default” NumPy type, so it gets the mosttesting, and is the type most likely to be returned by 3rd partycode that uses NumPy.:) Is quite at home handling data of any number of dimensions.:) Closer in semantics to tensor algebra, if you are familiarwith that.:)All operations (*,/,+,- etc.) areelement-wise.:( Sparse matrices fromscipy.sparse do not interact as wellwith arrays.matrix:\\ Behavior is more like that of MATLAB® matrices.<:( Maximum of two-dimensional. To hold three-dimensional data youneedarray or perhaps a Python list ofmatrix.<:( Minimum of two-dimensional. You cannot have vectors. They must becast as single-column or single-row matrices.<:( Sincearray is the default in NumPy, some functions mayreturn anarray even if you give them amatrix as anargument. This shouldn’t happen with NumPy functions (if it doesit’s a bug), but 3rd party code based on NumPy may not honor typepreservation like NumPy does.:)A*B is matrix multiplication, so it looks just like you writeit in linear algebra (For Python >= 3.5 plain arrays have the sameconvenience with the@ operator).<:( Element-wise multiplication requires calling a function,multiply(A,B).<:( The use of operator overloading is a bit illogical:*does not work element-wise but/ does.scipy.sparse is a bit cleaner.Thearray is thus much more advisable to use. Indeed, we intend todeprecatematrix eventually.
The table below gives rough equivalents for some common MATLAB®expressions.These are not exact equivalents, but rather should betaken as hints to get you going in the right direction. For more detailread the built-in documentation on the NumPy functions.
In the table below, it is assumed that you have executed the followingcommands in Python:
fromnumpyimport*importscipy.linalg
Also assume below that if the Notes talk about “matrix” that thearguments are two-dimensional entities.
| MATLAB | numpy | Notes |
|---|---|---|
helpfunc | info(func) orhelp(func) orfunc? (in Ipython) | get help on the functionfunc |
whichfunc | see note HELP | find out wherefunc is defined |
typefunc | source(func) orfunc?? (in Ipython) | print source forfunc (if not a native function) |
a&&b | aandb | short-circuiting logical AND operator (Python native operator);scalar arguments only |
a||b | aorb | short-circuiting logical OR operator (Python native operator);scalar arguments only |
1*i,1*j,1i,1j | 1j | complex numbers |
eps | np.spacing(1) | Distance between 1 and the nearest floating point number. |
ode45 | scipy.integrate.solve_ivp(f) | integrate an ODE with Runge-Kutta 4,5 |
ode15s | scipy.integrate.solve_ivp(f,method='BDF') | integrate an ODE with BDF method |
| MATLAB | NumPy | Notes |
|---|---|---|
ndims(a) | ndim(a) ora.ndim | get the number of dimensions of an array |
numel(a) | size(a) ora.size | get the number of elements of an array |
size(a) | shape(a) ora.shape | get the “size” of the matrix |
size(a,n) | a.shape[n-1] | get the number of elements of the n-th dimension of arraya. (Notethat MATLAB® uses 1 based indexing while Python uses 0 based indexing,See noteINDEXING) |
[123;456] | array([[1.,2.,3.],[4.,5.,6.]]) | 2x3 matrix literal |
[ab;cd] | block([[a,b],[c,d]]) | construct a matrix from blocksa,b,c, andd |
a(end) | a[-1] | access last element in the 1xn matrixa |
a(2,5) | a[1,4] | access element in second row, fifth column |
a(2,:) | a[1] ora[1,:] | entire second row ofa |
a(1:5,:) | a[0:5] ora[:5] ora[0:5,:] | the first five rows ofa |
a(end-4:end,:) | a[-5:] | the last five rows ofa |
a(1:3,5:9) | a[0:3][:,4:9] | rows one to three and columns five to nine ofa. This givesread-only access. |
a([2,4,5],[1,3]) | a[ix_([1,3,4],[0,2])] | rows 2,4 and 5 and columns 1 and 3. This allows the matrix to bemodified, and doesn’t require a regular slice. |
a(3:2:21,:) | a[2:21:2,:] | every other row ofa, starting with the third and going to thetwenty-first |
a(1:2:end,:) | a[::2,:] | every other row ofa, starting with the first |
a(end:-1:1,:) orflipud(a) | a[::-1,:] | a with rows in reverse order |
a([1:end1],:) | a[r_[:len(a),0]] | a with copy of the first row appended to the end |
a.' | a.transpose() ora.T | transpose ofa |
a' | a.conj().transpose() ora.conj().T | conjugate transpose ofa |
a*b | a@b | matrix multiply |
a.*b | a*b | element-wise multiply |
a./b | a/b | element-wise divide |
a.^3 | a**3 | element-wise exponentiation |
(a>0.5) | (a>0.5) | matrix whose i,jth element is (a_ij > 0.5). The Matlab result is anarray of 0s and 1s. The NumPy result is an array of the booleanvaluesFalse andTrue. |
find(a>0.5) | nonzero(a>0.5) | find the indices where (a > 0.5) |
a(:,find(v>0.5)) | a[:,nonzero(v>0.5)[0]] | extract the columms ofa where vector v > 0.5 |
a(:,find(v>0.5)) | a[:,v.T>0.5] | extract the columms ofa where column vector v > 0.5 |
a(a<0.5)=0 | a[a<0.5]=0 | a with elements less than 0.5 zeroed out |
a.*(a>0.5) | a*(a>0.5) | a with elements less than 0.5 zeroed out |
a(:)=3 | a[:]=3 | set all values to the same scalar value |
y=x | y=x.copy() | numpy assigns by reference |
y=x(2,:) | y=x[1,:].copy() | numpy slices are by reference |
y=x(:) | y=x.flatten() | turn array into vector (note that this forces a copy) |
1:10 | arange(1.,11.) orr_[1.:11.] orr_[1:10:10j] | create an increasing vector (see noteRANGES) |
0:9 | arange(10.) orr_[:10.] orr_[:9:10j] | create an increasing vector (see noteRANGES) |
[1:10]' | arange(1.,11.)[:,newaxis] | create a column vector |
zeros(3,4) | zeros((3,4)) | 3x4 two-dimensional array full of 64-bit floating point zeros |
zeros(3,4,5) | zeros((3,4,5)) | 3x4x5 three-dimensional array full of 64-bit floating point zeros |
ones(3,4) | ones((3,4)) | 3x4 two-dimensional array full of 64-bit floating point ones |
eye(3) | eye(3) | 3x3 identity matrix |
diag(a) | diag(a) | vector of diagonal elements ofa |
diag(a,0) | diag(a,0) | square diagonal matrix whose nonzero values are the elements ofa |
rand(3,4) | random.rand(3,4) | random 3x4 matrix |
linspace(1,3,4) | linspace(1,3,4) | 4 equally spaced samples between 1 and 3, inclusive |
[x,y]=meshgrid(0:8,0:5) | mgrid[0:9.,0:6.] ormeshgrid(r_[0:9.],r_[0:6.] | two 2D arrays: one of x values, the other of y values |
ogrid[0:9.,0:6.] orix_(r_[0:9.],r_[0:6.] | the best way to eval functions on a grid | |
[x,y]=meshgrid([1,2,4],[2,4,5]) | meshgrid([1,2,4],[2,4,5]) | |
ix_([1,2,4],[2,4,5]) | the best way to eval functions on a grid | |
repmat(a,m,n) | tile(a,(m,n)) | create m by n copies ofa |
[ab] | concatenate((a,b),1) orhstack((a,b)) orcolumn_stack((a,b)) orc_[a,b] | concatenate columns ofa andb |
[a;b] | concatenate((a,b)) orvstack((a,b)) orr_[a,b] | concatenate rows ofa andb |
max(max(a)) | a.max() | maximum element ofa (with ndims(a)<=2 for matlab) |
max(a) | a.max(0) | maximum element of each column of matrixa |
max(a,[],2) | a.max(1) | maximum element of each row of matrixa |
max(a,b) | maximum(a,b) | comparesa andb element-wise, and returns the maximum valuefrom each pair |
norm(v) | sqrt(v@v) ornp.linalg.norm(v) | L2 norm of vectorv |
a&b | logical_and(a,b) | element-by-element AND operator (NumPy ufunc)See noteLOGICOPS |
a|b | logical_or(a,b) | element-by-element OR operator (NumPy ufunc)See note LOGICOPS |
bitand(a,b) | a&b | bitwise AND operator (Python native and NumPy ufunc) |
bitor(a,b) | a|b | bitwise OR operator (Python native and NumPy ufunc) |
inv(a) | linalg.inv(a) | inverse of square matrixa |
pinv(a) | linalg.pinv(a) | pseudo-inverse of matrixa |
rank(a) | linalg.matrix_rank(a) | matrix rank of a 2D array / matrixa |
a\b | linalg.solve(a,b) ifa is square;linalg.lstsq(a,b)otherwise | solution of a x = b for x |
b/a | Solve a.T x.T = b.T instead | solution of x a = b for x |
[U,S,V]=svd(a) | U,S,Vh=linalg.svd(a),V=Vh.T | singular value decomposition ofa |
chol(a) | linalg.cholesky(a).T | cholesky factorization of a matrix (chol(a) in matlab returns anupper triangular matrix, butlinalg.cholesky(a) returns a lowertriangular matrix) |
[V,D]=eig(a) | D,V=linalg.eig(a) | eigenvalues and eigenvectors ofa |
[V,D]=eig(a,b) | V,D=np.linalg.eig(a,b) | eigenvalues and eigenvectors ofa,b |
[V,D]=eigs(a,k) | find thek largest eigenvalues and eigenvectors ofa | |
[Q,R,P]=qr(a,0) | Q,R=scipy.linalg.qr(a) | QR decomposition |
[L,U,P]=lu(a) | L,U=scipy.linalg.lu(a) orLU,P=scipy.linalg.lu_factor(a) | LU decomposition (note: P(Matlab) == transpose(P(numpy)) ) |
conjgrad | scipy.sparse.linalg.cg | Conjugate gradients solver |
fft(a) | fft(a) | Fourier transform ofa |
ifft(a) | ifft(a) | inverse Fourier transform ofa |
sort(a) | sort(a) ora.sort() | sort the matrix |
[b,I]=sortrows(a,i) | I=argsort(a[:,i]),b=a[I,:] | sort the rows of the matrix |
regress(y,X) | linalg.lstsq(X,y) | multilinear regression |
decimate(x,q) | scipy.signal.resample(x,len(x)/q) | downsample with low-pass filtering |
unique(a) | unique(a) | |
squeeze(a) | a.squeeze() |
Submatrix: Assignment to a submatrix can be done with lists ofindexes using theix_ command. E.g., for 2d arraya, one mightdo:ind=[1,3]; a[np.ix_(ind,ind)]+=100.
HELP: There is no direct equivalent of MATLAB’swhich command,but the commandshelp andsource will usually list the filenamewhere the function is located. Python also has aninspect module (doimport inspect) which provides agetfile that often works.
INDEXING: MATLAB® uses one based indexing, so the initial elementof a sequence has index 1. Python uses zero based indexing, so theinitial element of a sequence has index 0. Confusion and flamewars arisebecause each has advantages and disadvantages. One based indexing isconsistent with common human language usage, where the “first” elementof a sequence has index 1. Zero based indexingsimplifiesindexing.See alsoa text by prof.dr. Edsger W.Dijkstra.
RANGES: In MATLAB®,0:5 can be used as both a range literaland a ‘slice’ index (inside parentheses); however, in Python, constructslike0:5 canonly be used as a slice index (inside squarebrackets). Thus the somewhat quirkyr_ object was created to allownumpy to have a similarly terse range construction mechanism. Note thatr_ is not called like a function or a constructor, but ratherindexed using square brackets, which allows the use of Python’s slicesyntax in the arguments.
LOGICOPS: & or | in NumPy is bitwise AND/OR, while in Matlab &and | are logical AND/OR. The difference should be clear to anyone withsignificant programming experience. The two can appear to work the same,but there are important differences. If you would have used Matlab’s &or | operators, you should use the NumPy ufuncslogical_and/logical_or. The notable differences between Matlab’s andNumPy’s & and | operators are:
If you know you have boolean arguments, you can get away with usingNumPy’s bitwise operators, but be careful with parentheses, like this: z= (x > 1) & (x < 2). The absence of NumPy operator forms of logical_andand logical_or is an unfortunate consequence of Python’s design.
RESHAPE and LINEAR INDEXING: Matlab always allows multi-dimensionalarrays to be accessed using scalar or linear indices, NumPy does not.Linear indices are common in Matlab programs, e.g. find() on a matrixreturns them, whereas NumPy’s find behaves differently. When convertingMatlab code it might be necessary to first reshape a matrix to a linearsequence, perform some indexing operations and then reshape back. Asreshape (usually) produces views onto the same storage, it should bepossible to do this fairly efficiently. Note that the scan order used byreshape in NumPy defaults to the ‘C’ order, whereas Matlab uses theFortran order. If you are simply converting to a linear sequence andback this doesn’t matter. But if you are converting reshapes from Matlabcode which relies on the scan order, then this Matlab code: z =reshape(x,3,4); should become z = x.reshape(3,4,order=’F’).copy() inNumPy.
In MATLAB® the main tool available to you for customizing theenvironment is to modify the search path with the locations of yourfavorite functions. You can put such customizations into a startupscript that MATLAB will run on startup.
NumPy, or rather Python, has similar facilities.
PYTHONPATH environment variable.PYTHONSTARTUP environmentvariable to contain the name of your startup script.Unlike MATLAB®, where anything on your path can be called immediately,with Python you need to first do an ‘import’ statement to make functionsin a particular file accessible.
For example you might make a startup script that looks like this (Note:this is just an example, not a statement of “best practices”):
# Make all numpy available via shorter 'num' prefiximportnumpyasnum# Make all matlib functions accessible at the top level via M.func()importnumpy.matlibasM# Make some matlib functions accessible directly at the top level via, e.g. rand(3,3)fromnumpy.matlibimportrand,zeros,ones,empty,eye# Define a Hermitian functiondefhermitian(A,**kwargs):returnnum.transpose(A,**kwargs).conj()# Make some shortcuts for transpose,hermitian:# num.transpose(A) --> T(A)# hermitian(A) --> H(A)T=num.transposeH=hermitian
Seehttp://mathesaurus.sf.net/ for another MATLAB®/NumPycross-reference.
An extensive list of tools for scientific work with python can befound in thetopical software page.
MATLAB® and SimuLink® are registered trademarks of The MathWorks.