将Numpy数组转换为Matlab格式,反之亦然

47

我正在寻找一种将NumPy数组传递给Matlab的方法。

我已经通过使用scipy.misc.imsave将数组存储为图像,然后使用imread加载它来实现了这一点,但是这会导致矩阵包含0到256之间的值而不是“真实”值。

将此矩阵除以256,并乘以原始NumPy数组中的最大值可以得到正确的矩阵,但我觉得这有点繁琐。

是否有更简单的方法?


3
我忘了,Matlab是否可以解析文本文件?因为你可以将numpy数组格式化为Matlab风格的字符串,并将它们写入文件,然后再将这些数组读入Matlab中。 - JAB
1
你有没有考虑使用 mlabwrap http://mlabwrap.sourceforge.net/#description - dilip kumbham
2
你确定你不能完全使用numpy/scipy进行计算吗?只是好奇。 - Bort
2
MATLAB可以读写HDF5格式,而且有Python库。 - Memming
相关问题:https://www.mathworks.com/matlabcentral/answers/359680-can-someone-provide-me-an-example-of-loading-data-from-python-to-matlab请提供一个从Python到Matlab加载数据的示例。 - Charlie Parker
显示剩余2条评论
10个回答

61
当然,只需要使用scipy.io.savemat
例如:
import numpy as np
import scipy.io

x = np.linspace(0, 2 * np.pi, 100)
y = np.cos(x)

scipy.io.savemat('test.mat', dict(x=x, y=y))

类似的,还有scipy.io.loadmat

你可以使用load test在Matlab中加载它。

或者像@JAB建议的那样,你可以将数据保存为ASCII制表符分隔文件(例如numpy.savetxt)。但是,如果选择这种方法,您将被限制在2个维度。另一方面,ASCII是通用交换格式。几乎任何东西都能处理一个分隔文本文件。


这样可以将一个numpy数组保存到文件中,然后用类似于load('test.mat')的方式在matlab中读取吗? - Charlie Parker
使用 data.npzdata.npy 不起作用吗?如果不行,为什么? - Charlie Parker
2
谢谢,这正是我需要的。 - arilwan

13

一个简单的解决方案,无需通过文件或外部库传递数据。

Numpy有一个将ndarray转换为list的方法,而matlab数据类型可以从列表中定义。因此,可以这样转换:

np_a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
mat_a = matlab.double(np_a.tolist())

从Matlab到Python需要更多的关注。没有内置函数可以直接将类型转换为列表。但是我们可以访问未形成结构的原始数据,因此使用reshape(正确格式化)和transpose(由于MATLAB和numpy存储数据的不同方式)来处理。强调一下:在您的项目中进行测试非常重要,特别是如果您正在使用超过2个维度的矩阵。它适用于MATLAB 2015a和2个维度。

np_a = np.array(mat_a._data.tolist())
np_a = np_a.reshape(mat_a.size).transpose()

3
注意,mat_a = matlab.double(np_a.tolist()) 可能非常低效/慢。除了处理np数组之外,最好采用Joe Kington的答案。请参见 https://dev59.com/LlcO5IYBdhLWcg3wvUQp#45284125。 - 5Ke

6
以下是翻译的结果:

这里有一个解决方案,避免在Python中迭代或使用文件IO——但需要依赖于(丑陋的)Matlab内部结构:

import matlab
# This is actually `matlab._internal`, but matlab/__init__.py
# mangles the path making it appear as `_internal`.
# Importing it under a different name would be a bad idea.
from _internal.mlarray_utils import _get_strides, _get_mlsize

def _wrapper__init__(self, arr):
    assert arr.dtype == type(self)._numpy_type
    self._python_type = type(arr.dtype.type().item())
    self._is_complex = np.issubdtype(arr.dtype, np.complexfloating)
    self._size = _get_mlsize(arr.shape)
    self._strides = _get_strides(self._size)[:-1]
    self._start = 0

    if self._is_complex:
        self._real = arr.real.ravel(order='F')
        self._imag = arr.imag.ravel(order='F')
    else:
        self._data = arr.ravel(order='F')

_wrappers = {}
def _define_wrapper(matlab_type, numpy_type):
    t = type(matlab_type.__name__, (matlab_type,), dict(
        __init__=_wrapper__init__,
        _numpy_type=numpy_type
    ))
    # this tricks matlab into accepting our new type
    t.__module__ = matlab_type.__module__
    _wrappers[numpy_type] = t

_define_wrapper(matlab.double, np.double)
_define_wrapper(matlab.single, np.single)
_define_wrapper(matlab.uint8, np.uint8)
_define_wrapper(matlab.int8, np.int8)
_define_wrapper(matlab.uint16, np.uint16)
_define_wrapper(matlab.int16, np.int16)
_define_wrapper(matlab.uint32, np.uint32)
_define_wrapper(matlab.int32, np.int32)
_define_wrapper(matlab.uint64, np.uint64)
_define_wrapper(matlab.int64, np.int64)
_define_wrapper(matlab.logical, np.bool_)

def as_matlab(arr):
    try:
        cls = _wrappers[arr.dtype.type]
    except KeyError:
        raise TypeError("Unsupported data type")
    return cls(arr)

到达这里所需的观察是:

  • Matlab似乎只查看type(x).__name__type(x).__module__来确定是否理解类型。
  • 任何可索引对象都可以放置在._data属性中。

不幸的是,Matlab在内部没有有效地使用_data属性,并且正在逐个项目地迭代它,而不是使用Python的memoryview协议 :(. 因此,使用这种方法的速度增益很小。


2
加速应该相当显著。我用这个方法得到了约15倍的因素。https://dev59.com/LlcO5IYBdhLWcg3wvUQp#45290997 - max9111
@max9111:你发现用array.array包装(就像你在那里做的那样)和不这样做(就像我在这里做的那样)有什么区别吗? - Eric
是的,你的版本更快 ;). 或许最好澄清一下,你的解决方案实际上比 matlab.double(np_a.tolist()) 快得多。 - max9111
对于那些想要避免在文件系统中进行数据重复和任何I/O相关时间的情况来说,这似乎是最佳选择。 - NLi10Me
对于一个100 x 100 x 100的数组,该方法每次循环需要2.73毫秒±191微秒(平均值±7次运行的std.dev.,每次100个循环),但matlab.double每次循环需要1.12秒±58.1毫秒(平均值±7次运行的std.dev.,每次1个循环)。 大约提速了400倍... - ZK xxxxx

4

scipy.io.savemat或scipy.io.loadmat无法处理matlab数组--v7.3。但好的一面是,matlab--v7.3文件是hdf5数据集。因此,它们可以使用许多工具(包括numpy)进行读取。

对于python,您需要h5py扩展程序,这需要在您的系统上安装HDF5

import numpy as np, h5py 
f = h5py.File('somefile.mat','r') 
data = f.get('data/variable1') 
data = np.array(data) # For converting to numpy array

2

不确定是否算作“简单”,但我找到了一个解决方案,可以快速地从在Matlab中调用的Python脚本中创建的numpy数组中移动数据:

dump_reader.py(Python源代码):

import numpy

def matlab_test2():
    np_a    = numpy.random.uniform(low = 0.0, high = 30000.0, size = (1000,1000))
    return np_a

dump_read.m(Matlab脚本):

clear classes
mod = py.importlib.import_module('dump_reader');
py.importlib.reload(mod);

if count(py.sys.path,'') == 0
    insert(py.sys.path,int32(0),'');
end

tic
A = py.dump_reader.matlab_test2();
toc
shape = cellfun(@int64,cell(A.shape));
ls = py.array.array('d',A.flatten('F').tolist());
p = double(ls);
toc
C = reshape(p,shape);
toc

这取决于matlab的双精度在数组上相对于单元格/矩阵的高效工作。第二个技巧是通过Python的本地array.array以有效的方式将数据传递给matlab的双精度。

P.S. 抱歉挖坟,但我为此苦苦挣扎了很久,这个主题是最接近的结果之一。也许它可以帮助有所缩短难以解决问题的时间。

P.P.S. 已在 Matlab R2016b + python 3.5.4 (64位) 上测试。


2

不久以前,我遇到了同样的问题,并编写了以下脚本,以便轻松地在交互式会话之间复制和粘贴数组。显然,仅适用于小型数组,但我发现这比每次通过文件保存/加载更为方便:

Matlab -> Python

Python -> Matlab


2
Python库Darr允许您以二进制和文本文件的形式保存Python numpy数组,使其具有自说明性和广泛可读性。在保存数组时,它将包括用于在各种语言中读取该数组的代码,包括Matlab。因此,在Python中将您的numpy数组保存到磁盘只需要一行代码,然后从README.txt中复制粘贴代码即可将其加载到Matlab中。

声明:我编写了这个库。


1
有趣!如果您是编写该库的Gabriel,请添加披露。 - Cris Luengo

1
从MATLAB R2022a开始,matlab.double(以及matlab.int8、matlab.uint8等)对象实现了缓冲区协议。这意味着您可以将它们传递给NumPy数组构造函数。反向构建(即本问题的主题)也得到支持。也就是说,可以从实现缓冲区协议的对象构建matlab对象。因此,例如,可以从NumPy双精度数组构建matlab.double。
更新:此外,从MATLAB R2022b开始,实现缓冲区协议的对象(如NumPy对象)可以直接传递到通过Python调用的MATLAB函数中。来自R2022b的MATLAB Release Notes中的“外部语言接口”部分。
import matlab.engine
import numpy
eng = matlab.engine.start_matlab()
buf = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='uint16')

# Supported in R2022a and earlier: must initialize a matlab.uint16 from
# the numpy array and pass it to the function
array_as_matlab_uint16 = matlab.uint16(buf)
res = eng.sum(array_as_matlab_uint16, 1, 'native')
print(res)

# Supported as of R2022b: can pass the numpy array
# directly to the function
res = eng.sum(buf, 1, 'native')
print(res)

1
如果您能提供示例,那就太好了。 - fabiob
这其实很简单,可以在这里找到解释:https://de.mathworks.com/content/dam/mathworks/fact-sheet/using-matlab-with-python-cheat-sheet.pdf。 要将matlab数组MLA转换为python numpy数组,只需执行np.array(MLA)即可。 - fabiob
@fabiob,我添加了一个名为“UPDATE”的部分,其中包括一个示例。但是,您提到的信息表似乎实际上并没有涵盖MATLAB<->NumPy转换。您是不是想链接到其他东西? - Alan
是的...但它说“数据类型将在可能的情况下自动转换。”和“一些MATLAB数据类型需要转换”,所以它帮助我推断出解决方案(我对matlab->python方向感兴趣)。 - fabiob

0
在最新的R2021a版本中,您可以将Python NumPy ndarray传递给double()函数,它会将其转换为本地的MATLAB矩阵。即使在控制台调用NumPy数组,底部也会建议“使用double函数将其转换为MATLAB数组”。

0

假设您有一个形状为(365,10)的2D每日数据,保存在np数组np3Darrat中,该数组将具有形状(5,365,10),表示五年的数据。在Python中保存您的np数组:

import scipy.io as sio     #SciPy module to load and save mat-files
m['np3Darray']=np3Darray   #shape(5,365,10)
sio.savemat('file.mat',m)  #Save np 3D array 

然后在MATLAB中将np的3D数组转换为MATLAB的3D矩阵:

load('file.mat','np3Darray')
M3D=permute(np3Darray, [2 3 1]);   %Convert numpy array with shape (5,365,10) to MATLAB matrix with shape (365,10,5)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接