Search code examples
pythonarraysperformancematlabmultidimensional-array

Convert matlab.double array to python array


I am using the matlab python engine to access data from my matlab projects in python. This works quite well, but I do have a problem to efficiently use the matlab arrays in python. For example I want an array from matlab, I use (eng stands for the matlab engine):

x = eng.eval(arg)

What I get is a matlab.double array which looks like this:

matlab.double([[1.0,2.0],[4.0,3.0],[2.0,5.0]])

Doesn't look too bad. Let's try to catch an entry:

>>> x[2][1]
5.0

Yay! How about a full row?

>>> x[0]
matlab.double([1.0,2.0])

.. well, at least it's a row, but I'm not found of the "matlab.double" prefix.. how about a column?

>>> x[:][0]
matlab.double([1.0,2.0])

Wait, what? I try to select all rows, and then the first element from each, but instead I get just the row. And in fact:

x[i] == x[:][i] == x[i][:]

So, basically two problems arise: Selecting a row brings me the unwanted "matlab.double" prefix, and selecting a column (personally more important) doesn't work at all. Any suggestions here? What i did now, was to reread every value for itself and safe it into a new python array:

c = [[] for _ in range(len(x[0]))]
for i in range(len(x[0])):
    for j in range(len(x)):
        c[i].append(x[j][i])

This works, but there is one catch: It extremely slows the code down with growing data. And of course, it doesn't feel beautiful to reread every single entry, if they are in fact already stored in x.

Thanks for reading through this long text, I just assume I explain a little bit more since probably only a few people work with the python matlab engine.


Solution

  • A more generic approach, which I'm using productively now, also allowing me to round the values if needed (careful though, rounding takes more calculation time):

    from math import log10, floor
    
    def convert(self, x, ROUND=0):
    
        conv = []
    
        for _ in range(x.size[0]):
            lst = x._data[_::x.size[0]].tolist()
    
            if ROUND is not 0:
                lst = [self.round_sig(elem, ROUND) if elem != 0 and
                       elem == elem else elem for elem in lst]
    
            conv.append(lst)
    
        return conv
    
    def round_sig(self, x, sig=2):
        return round(x, sig-int(floor(log10(abs(x))))-1)