我们最喜欢的MATLAB / Octave编程技巧是什么?

时间:2020-03-06 14:42:32  来源:igfitidea点击:

我想每个人都会同意MATLAB语言不是很漂亮,或者特别是一致。但是没关系!我们仍然必须使用它来完成工作。

我们最喜欢的使事情变得容易的窍门是什么?让我们为每个答案提供一个答案,以便人们在同意的情况下可以将其投票。另外,尝试通过示例来说明答案。

解决方案

这是一个简单的例子:

我发现用逗号分隔的列表语法对于构建函数调用非常有用:

% Build a list of args, like so:
args = {'a', 1, 'b', 2};
% Then expand this into arguments:
output = func(args{:})

从Matlab调用Java代码

使用单个冒号将矩阵转换为向量。

x = rand(4,4);
x(:)

通过在帮助注释中添加"另请参阅"行,可以快速访问其他功能文档。首先,必须在所有大写字母中包含函数名称作为第一行注释。做通常的注释标题,然后用逗号分隔其他相关功能的列表放进去。

function y = transmog(x)
%TRANSMOG Transmogrifies a matrix X using reverse orthogonal eigenvectors
%
% Usage:
%   y = transmog(x)
%
% SEE ALSO
% UNTRANSMOG, TRANSMOG2

在命令行上输入" help transmog"时,我们将在此注释标题中看到所有注释,并带有指向列出的其他功能的注释标题的超链接。

cellfun和arrayfun用于自动for循环。

用于数组操作的冒号运算符。

@ ScottieT812,提到了一个:展平数组,但是还有其他所有选择数组位的变体:

x=rand(10,10);
flattened=x(:);
Acolumn=x(:,10);
Arow=x(10,:);

y=rand(100);
firstSix=y(1:6);
lastSix=y(end-5:end);
alternate=y(1:2:end);

使用nargin设置可选参数的默认值,并使用nargout设置可选输出参数。快速示例

function hLine=myplot(x,y,plotColor,markerType)
% set defaults for optional paramters
if nargin<4, markerType='none'; end
if nargin<3, plotColor='k'; end

hL = plot(x,y,'linetype','-', ...  
              'color',plotColor, ...
              'marker',markerType, ...
              'markerFaceColor',plotColor,'markerEdgeColor',plotColor);

% return handle of plot object if required
if nargout>0, hLine = hL; end

使用逻辑数组直接提取满足特定条件的矩阵元素:

x = rand(1,50) .* 100;
xpart = x( x > 20 & x < 35);

现在,xpart仅包含x处于指定范围内的那些元素。

使用内置的探查器查看我的代码的热门部分在哪里:

profile on
% some lines of code
profile off
profile viewer

或者仅使用内置的tic和toc来获得快速计时:

tic;
% some lines of code
toc;

使用ismember()合并由文本标识符组织的数据。当我们分析不同的条目(以公司符号为例)来回走动时,很有用。

%Merge B into A based on Text identifiers
UniverseA = {'A','B','C','D'};
UniverseB = {'A','C','D'};

DataA = [20 40 60 80];
DataB = [30 50 70];

MergeData = NaN(length(UniverseA),2);

MergeData(:,1) = DataA;

[tf, loc] = ismember(UniverseA, UniverseB);

MergeData(tf,2) = DataB(loc(tf));

 MergeData =

20    30
40   NaN
60    50
80    70

哦,反转数组

v = 1:10;
v_reverse = v(length(v):-1:1);

向量化循环。有很多方法可以做到这一点,并且在代码中寻找循环并查看如何将其向量化非常有趣。向量运算的性能惊人地快!

向量化:

function iNeedle = findClosest(hay,needle)
%FINDCLOSEST find the indicies of the closest elements in an array.
% Given two vectors [A,B], findClosest will find the indicies of the values
% in vector A closest to the values in vector B.
[hay iOrgHay] = sort(hay(:)');  %#ok must have row vector

% Use histogram to find indices of elements in hay closest to elements in
% needle. The bins are centered on values in hay, with the edges on the
% midpoint between elements.
[iNeedle iNeedle] = histc(needle,[-inf hay+[diff(hay)/2 inf]]); %#ok

% Reversing the sorting.
iNeedle = iOrgHay(iNeedle);

以下是一些不明显的函数,这些函数会不时有用:

  • mfilename(返回当前正在运行的MATLAB脚本的名称)
  • dbstack(允许我们访问matlab函数堆栈的名称和行号)
  • 键盘(停止执行并将控制权交给调试提示;这就是为什么在调试提示中出现K的原因。
  • dbstop error(自动使我们进入调试模式,停止在触发错误的行)

图形中公式的LaTeX模式:在最新版本(R2006?)之一中,我们在函数调用的末尾添加了添加参数,'Interpreter','latex',它将使用LaTeX渲染。这是一个例子:

t=(0:0.001:1);
plot(t,sin(2*pi*[t ; t+0.25]));
xlabel('t'); 
ylabel('$\hat{y}_k=sin 2\pi (t+{k \over 4})$','Interpreter','latex');
legend({'$\hat{y}_0$','$\hat{y}_1$'},'Interpreter','latex');

不知道他们何时添加它,但是它可以在text(),title(),xlabel(),ylabel(),zlabel()甚至legend()函数中与R2006b一起使用。只要确保我们使用的语法没有歧义即可(因此,对于legend(),我们需要将字符串指定为单元格数组)。

使用sim命令直接从脚本(而不是交互地)执行Simulink模型。我们可以执行以下操作,例如从工作空间变量中获取参数,然后在循环中反复运行" sim"以模拟某些事物,同时更改参数以查看行为如何变化,并使用所需的任何图形命令绘制结果图形。比尝试以交互方式进行操作要容易得多,并且在可视化结果时,它比Simulink"示波器"模块为我们提供了更大的灵活性。 (尽管我们不能使用它来实时查看模拟运行过程中发生的情况)

要知道的一个非常重要的事情是Simset命令的DstWorkspace和SrcWorkspace选项。这些控件控制"到工作区"和"来自工作区"块的获取和放置结果的位置。 " Dstworkspace"默认为当前工作空间(例如,如果我们从函数内部调用" sim",则" To Workspace"块将显示为可从同一函数访问的变量),但" SrcWorkspace"默认为基本工作空间,并且想要封装对sim的调用,我们需要将SrcWorkspace设置为current,因此提供了一个干净的接口来提供/获取仿真输入参数和输出。例如:

function Y=run_my_sim(t,input1,params)
% runs "my_sim.mdl" 
% with a From Workspace block referencing I1 as an input signal
% and parameters referenced as fields of the "params" structure
% and output retrieved from a To Workspace block with name O1.
opt = simset('SrcWorkspace','current','DstWorkspace','current');
I1 = struct('time',t,'signals',struct('values',input1,'dimensions',1));
Y = struct;
Y.t = sim('my_sim',t,opt);
Y.output1 = O1.signals.values;

具有[[c,h] = contourclabel(c,h,'fontsize',fontsize)的轮廓图。我通常使用fontsize`参数来减小字体大小,以使数字不会相互碰到。这对于查看2D函数的值非常有用,而无需弄乱3D图形。

匿名函数,原因如下:

  • 快速完成一次性功能,例如3x ^ 2 + 2x + 7. (请参见下面的清单)这对于将quad和fminbnd之类的函数作为参数的函数很有用。在脚本(不以函数头开头的.m文件)中,它也很方便,因为与真实函数不同,我们不能包含子函数。
  • 用于闭包-尽管匿名函数有一些限制,因为似乎没有一种在其中进行赋值以改变状态的方法。

% quick functions
f = @(x) 3*x.^2 + 2*x + 7;
t = (0:0.001:1);
plot(t,f(t),t,f(2*t),t,f(3*t));

% closures (linfunc below is a function that returns a function,
% and the outer functions arguments are held for the lifetime
% of the returned function.
linfunc = @(m,b) @(x) m*x+b;
C2F = linfunc(9/5, 32);
F2C = linfunc(5/9, -32*5/9);

赋值左侧的条件参数:

t = (0:0.005:10)';
x = sin(2*pi*t);
x(x>0.5 & t<5) = 0.5;
% This limits all values of x to a maximum of 0.5, where t<5
plot(t,x);

我出于很多原因喜欢使用函数句柄。首先,它们是我在MATLAB中找到的最接近指针的东西,因此我们可以为对象创建类似引用的行为。我们也可以使用它们进行一些简单(更简单)的操作。例如,替换switch语句:

switch number,
  case 1,
    outargs = fcn1(inargs);
  case 2,
    outargs = fcn2(inargs);
  ...
end
%
%can be turned into
%
fcnArray = {@fcn1, @fcn2, ...};
outargs = fcnArray{number}(inargs);

我只是认为这样的小事很酷。

了解轴属性!我们可以设置各种方法来调整默认的绘图属性以执行所需的操作:

set(gca,'fontsize',8,'linestyleorder','-','linewidth',0.3,'xtick',1:2:9);

(例如,将fontsize设置为8pt,将所有新行的线型都设置为纯色,宽度设置为0.3pt,xtick点设置为[1 3 5 7 9])

线和图形属性也很有用,但是我发现自己最常使用轴属性。

Matlab的bsxfun,arrayfun,cellfun和structfun非常有趣,并且经常保存一个循环。

M = rand(1000, 1000);
v = rand(1000,    1);
c = bsxfun(@plus, M, v);

例如,此代码将列向量v添加到矩阵M的每一列。

但是,在应用程序的性能至关重要的部分中,我们应该将这些功能与普通的for循环进行基准比较,因为通常循环仍会更快。

使用最小,最大,均值,差异,总和,任意,全部,...等聚合函数时,请严格指定尺寸

例如,该行:

reldiff = diff(a) ./ a(1:end-1)

计算向量中元素的相对差异可能会很好,但是如果向量退化为一个元素,则计算将失败:

>> a=rand(1,7);
>> diff(a) ./ a(1:end-1)

ans =
   -0.5822   -0.9935  224.2015    0.2708   -0.3328    0.0458

>> a=1;
>> diff(a) ./ a(1:end-1)
??? Error using ==> rdivide
Matrix dimensions must agree.

如果为函数指定了正确的尺寸,则此行将返回一个空的1×0矩阵,这是正确的:

>> diff(a, [], 2) ./ a(1, 1:end-1)

ans =

   Empty matrix: 1-by-0

>>

最小值函数的情况也一样,通常在矩阵的各列上计算最小值,直到矩阵仅由一行组成。然后,它将返回该行中的最小值,除非Dimension参数另有说明,这可能会破坏应用程序。

我几乎可以保证,因此设置这些聚合函数的尺寸将为我们节省大量的调试工作。

至少对我来说就是这种情况。 :)

令人惊讶的是,尽管人们提到了索引数组的逻辑数组方法,但没有人提及find命令。

例如如果x是NxMxO数组

x(x> 20)通过生成一个NxMxO逻辑数组并将其用于索引x来工作(如果我们有大数组并且正在寻找一个小子集,这可能会很糟糕

x(find(x> 20))的工作方式是生成满足x> 20的x的索引列表(即1xwhatever),并以此索引x。根据我的经验,应该多使用"查找"。

我称之为"技巧"的更多内容

如果我们不知道所需的大小,则可以使用end + 1来增长/追加到数组和单元格数组(只要切片的大小匹配,也可以使用更大的尺寸,因此,在这种情况下,将x初始化为[]以外的其他值)。对数字不利,但对事物(或者单元格数组)的小型动态列表不利,例如解析文件。

例如

>> x=[1,2,3]
x =  1     2     3
>> x(end+1)=4
x =  1     2     3     4

另一个认为很多人不知道的是,对于在任何暗1阵列上的作品,因此继续该示例

>> for n = x;disp(n);end
     1
     2
     3
     4

这意味着,如果我们需要的只是x的成员,则无需为它们建立索引。

这也适用于单元格数组,但是有点令人讨厌,因为在遍历它们时,元素仍然包裹在单元格中:

>> for el = {1,2,3,4};disp(el);end
    [1]
    [2]
    [3]
    [4]

因此,要获取元素,我们必须将其下标

>> for el = {1,2,3,4};disp(el{1});end
     1
     2
     3
     4

我不记得有没有更好的办法解决这个问题。

-我们可以创建Matlab快捷方式到名为startup.m的初始化文件。在这里,我定义了Matlab会话的格式,输出精度和绘图参数(例如,我使用了较大的绘图轴/字体大小,以便在演示文稿时可以清晰地看到.fig。)一位开发人员关于此的博客文章http://blogs.mathworks.com/loren/2009/03/03/whats-in-your-startupm/。

-我们可以使用"加载"功能加载整个数字ascii文件。这不是特别快,但是可以快速完成原型制作工作(那不是Matlab的座右铭吗?)

-如前所述,冒号运算符和向量化是救命稻草。螺丝环。

使用xlim和ylim绘制垂直和水平线。例子:

  • 在y = 10处画一条水平线:line(xlim,[10 10])
  • 在x = 5处绘制垂直线:line([5 5],ylim)

为了能够快速测试功能,我使用nargin像这样:

function result = multiply(a, b)
if nargin == 0 %no inputs provided, run using defaults for a and b
    clc;
    disp('RUNNING IN TEST MODE')
    a = 1;
    b = 2;
end

result = a*b;

稍后,我添加了一个单元测试脚本来针对不同的输入条件测试该功能。

x=repmat([1:10],3,1); % say, x is an example array of data
  
  l=x>=3; % l is a logical vector (1s/0s) to highlight those elements in the array that would meet a certain condition.
  
  N=sum(sum(l));% N is the number of elements that meet that given condition.

欢呼声-脚本编写愉快!