3
$\begingroup$

I was reading this code, for implemnting linear regression from scratch:

# convert from data frames to numpy matrices
X = np.matrix(X.values)
y = np.matrix(y.values)
theta = np.matrix(np.array([0,0]))

When I came accross this line : np.matrix(np.array([0,0]))

I was wondering why didn't the person just write np.matrix([0,0]).

I ran both in jupyter notebook and got the same output:

theta = np.matrix([0,0])
theta2 = np.matrix(np.array([0,0]))
print(theta,theta2,type(theta),type(theta2))

Output:[[0 0]] [[0 0]] <class 'numpy.matrix'> <class 'numpy.matrix'>

Is there a difference between the two? Does the extra np.array somehow part add to the functionality of theta? Will the final code function properly if I replace the former with the latter?

Thanks.

Edit: Is this the right place to ask this question? I am new here...

$\endgroup$
  • 2
    $\begingroup$ Welcome to the site! Even though the context of your question is data science related, your actual question is about how numpy works. It should probably be posted in Stack Overflow rather than here. $\endgroup$ – timleathart May 30 '19 at 4:12
  • $\begingroup$ OK. Thanks for the reply! $\endgroup$ – Yatin May 30 '19 at 4:18
3
$\begingroup$

No, they're absolutely the same.

In this case there is absolutely no difference apart from perhaps a trivial amount of processing time. This is all open source code so we can just read it:

The relevant part of numpy for us here is the matrix constructor (yes, np.matrix is a python class below the hood). In a summary from the NumPy code we see:

class matrix(N.ndarray):
# ...
    def __new__(subtype, data, dtype=None, copy=True):
    # ...
        if isinstance(data, N.ndarray):
            if dtype is None:
                intype = data.dtype
            else:
                intype = N.dtype(dtype)
            new = data.view(subtype)
            if intype != data.dtype:
                return new.astype(intype)
            if copy: return new.copy()
            else: return new
        # ...
        arr = N.array(data, dtype=dtype, copy=copy)
        ndim = arr.ndim
        shape = arr.shape
        # some extra checks
        ret = N.ndarray.__new__(subtype, shape, arr.dtype,
                                buffer=arr,
                                order=order)
        return ret

What we give as the data argument is literally what we give to to np.matrix(). Therefore we can draw for the two cases:

np.matrix([0, 0])

  1. The python interpreter builds two integers: 0 and 0.
  2. The python interpreter builds a list from the pointers to the two integers.
  3. The python interpreter evaluates the matrix constructor with the list as data.
  4. The if in the constructor is not executed, instead an np.array is build from the list.
  5. Inside the array constructor data types are checked.
  6. The final array is returned (the second array constructor perform much less work because it is passed buffer=)

np.matrix(np.array([0, 0]))

  1. The python interpreter builds two integers: 0 and 0.
  2. The python interpreter builds a list from the pointers to the two integers.
  3. The python interpreter evaluates the array constructor.
  4. The resulting array is passed as data to the matrix constructor, and the if is executed.
  5. Within the if the data type is taken from the existing array.
  6. The array is copied an returned.

Both ways execute pretty much the same number of constructors and lines of code. One could argue that copying the array (the copy= argument) may be a slow operation. Yet, given the fact that to have enough data for array.copy() to be slow one would first need to construct a full python list of that size, the copy() time is negligible compared to the list construction. In other words, both methods need to construct the list - because python will always evaluate arguments before passing them - which is the slowest part of the execution of this code.

As for the return value, they're absolutely and completely the same. Most of the code within the constructor summarized (and linked) above is to make sure that you get the same return if you give equivalent input.


P.S. (Unrelated Note) If one starts reading data from a file (or any other external source) the picture changes. If one reads directly into an array without going through the python list phase, that method is bound to be much faster. The processing bottleneck is the python list, if one can avoid that things will go faster.

$\endgroup$
1
$\begingroup$

As you definitely know the type of $np.array([0,0])$ is numpy.ndarray and the type of $[0,0]$ is list.

And using numpy ndarrays instead of lists is way faster. Further, numpy arrays consume smaller memory. Hence, due to a higher speed and memory usage, etc. (functionality), it is better to use numpy arrays instead of lists.

$\endgroup$
  • $\begingroup$ What you've said is technically correct, but in this context there is no difference between initialising a np.matrix with a list or an np.array. $\endgroup$ – timleathart May 30 '19 at 4:11
  • $\begingroup$ Yes, in this context and for this small array there is no difference for any of them. But there can't be any reasons for this choice rather than the general purpose that I mentioned. $\endgroup$ – Fatemeh Asgarinejad May 30 '19 at 5:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.