The `Optimizing and debugging code` part is where most data scientists falter.
So, this is a really nice effort for bringing it all together!
x.T * y # nope
x.dot(y) # still no
x.inner(y) # ok
What's likely happening is that you're using a 1D array and a 2D array. If they were both actually vectors, `x.dot(y)` would work fine.
import numpy as np
x = y = np.arange(10)
x.dot(y) # Yields 285
So, in your case, you probably had something like:
import numpy as np
x = np.arange(10)
y = x[np.newaxis, :]
x.dot(y) # Raises a ValueError
`np.dot` uses the last dimension of the first array (10) and the second-to-last dimension of the other array (1). They don't match. It's that simple.
You might have also been trying to take the dot product of two 2D arrays with the same dimension. In that case, the same thing happens. The last dimension of one doesn't match the second-to-last dimension of the other.
When I switched from MATLAB to Python/numpy/scipy, I had to deal with these syntactic annoyances. But now I feel comfortable after using it for many years.
Don't forget the biggest edge Python has over MATLAB for numeric simulation, i.e., you can prototype your work as quickly, and for the same program, convert the slow portions into C or Fortran without the need to rewrite the whole thing. Not to mention, a proper programming language underlying the numeric system.
That statement applies equally to Matlab/Octave. You can link C/C++/Fortran routines into Matlab code using its "mex" interface. Octave also supports this interface.
Python is more flexible, but less simple to start out with because it has a more complicated ecosystem. For example, there are many different ways to link in fast code (ctypes/cffi/cython/numba/weave/f2py/...). Moreover, the data in a numpy array can be stored in memory in different ways (C or Fortran order, not necessarily contiguous, ...), which requires some care when passing it to external routines.
The second, (the matrix product between `x` and `y`) works provided that the dimensions of `x` and `y` make sense to perform matrix multiplication, i.e., `x` is 1xN and `y` is Nx1.
While I agree there are some idiosyncrasies, I think it is hyperbole to call it a disaster. I will say that one idiosyncrasy that annoyed me up when moving from octave / matlab, was that NumPy's `zeros` and `ones` functions require the dimensions as a tuple, while others, like `rand` or `randn` take multiple arguments as the dimensions.