【问题标题】:What is your favourite MATLAB/Octave programming trick? [closed]你最喜欢的 MATLAB/Octave 编程技巧是什么? [关闭]
【发布时间】:2010-09-13 00:34:37
【问题描述】:

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

您最喜欢的让事情变得更简单的技巧是什么?让我们每个答案都有一个,这样人们可以在他们同意的情况下投票。另外,试着用一个例子来说明你的答案。

【问题讨论】:

    标签: matlab octave


    【解决方案1】:

    这是一个简单的例子:

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

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

    【讨论】:

    • 不确定 MATLAB,但在 Octave 中,您可以以类似的方式为多个变量赋值:[one two three four] = {1 2 3 4}{:}
    【解决方案2】:

    从 Matlab 调用 Java 代码

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 子矩阵你会怎么做?假设:x = rand(20, 20);我想把 x(1:10, 1:10) 变成一个向量。是 y=reshape(x(:10, 1:10), [], 1) 或 y=x(1:10, 1:10)-> y=y(:) 我唯一的选择吗?不用说 x(1:10, 1:10)(:) 不起作用。
      • @Drazick,您可以使用多维索引或单维索引访问 x 的元素。 myElems = [1:10 21:30 31:40...181:190]; y = x(myElems);
      • 假设我有一个图像 - I. 计算它的方差我会做:var(I(:))。如果我想计算其中一部分的方差怎么办 - I(1:20, 1:20)。 var(var(I(1:20, 1:20)) 不会这样做(这是错误的)。我知道的选项,y = I(1:20, 1:20) -> var(y(: )) 或 y=reshape(I(1:20, 1:20), [], 1) -> var(y(:))。我要问的是有没有办法在子矩阵上应用冒号运算符一个矩阵而不重新分配它?谢谢。
      • @Drazick:你应该把它作为一个单独的问题。另外,也许sub2ind 可以提供帮助
      • @Drazick: feval(@(x) x(:), x(1:3,1:3))
      【解决方案4】:

      通过在帮助 cmets 中添加“SEE ALSO”行,可以快速访问其他函数文档。首先,您必须将函数名称全部大写作为第一行注释。做你通常的评论标题的东西,然后用逗号分隔的其他相关函数的列表放入 SEE ALSO。

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

      当您在命令行键入“help transmog”时,您将在此注释标题中看到所有 cmets,并带有指向列出的其他函数的注释标题的超链接。

      【讨论】:

        【解决方案5】:

        cellfun 和 arrayfun 用于自动 for 循环。

        【讨论】:

          【解决方案6】:

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

          @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);
          

          【讨论】:

          • lastSix = y(end-5:end);您的版本返回 7 个元素。
          【解决方案7】:

          使用 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
          

          【讨论】:

          • 我发现使用if exist('plotColor', 'var') ... 的函数更易于维护,因为您使用的是参数的名称,而不仅仅是参数编号。
          【解决方案8】:

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

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

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

          【讨论】:

          • 在matlab中,你可以使用find函数来做基本相同的事情。
          • 但是 find 慢得多。逻辑索引要快得多,除非您需要知道匹配项的索引。
          【解决方案9】:

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

          profile on
          % some lines of code
          profile off
          profile viewer
          

          或者只是使用内置的tictoc 来获得快速计时:

          tic;
          % some lines of code
          toc;
          

          【讨论】:

            【解决方案10】:

            使用 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
            

            【讨论】:

              【解决方案11】:

              哦,还有反转一个数组

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

              【讨论】:

              • 嗯。我会使用flipud()fliplr() 来执行此操作。但是,结合步骤,这更有用。例如例如 v(end:-4:1)。
              • 我喜欢我的方式 vs. flipud()/fliplr() 因为你不必知道你有一个列向量还是一个行向量。
              • 您可以放弃 length() 调用并编写 v_reverse = v(end:-1:1);
              【解决方案12】:

              Vectorizing loops。有很多方法可以做到这一点,在你的代码中寻找循环并看看它们如何被向量化是很有趣的。向量运算的性能惊人地快!

              【讨论】:

              • 既然 Matlab 有 JIT 编译器,情况仍然如此吗?看看会很有趣。
              【解决方案13】:

              矢量化:

              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);
              

              【讨论】:

                【解决方案14】:

                这里有一堆不时有用的非显而易见的函数:

                • mfilename(返回当前运行的 MATLAB 脚本的名称)
                • dbstack(让您可以访问 matlab 函数堆栈的名称和行号)
                • keyboard(停止执行并将控制权交给调试提示符;这就是为什么在调试提示符K&gt;&gt; 中有一个 K 的原因
                • dbstop error(自动将您置于调试模式,在触发错误的行停止)

                【讨论】:

                  【解决方案15】:

                  LaTeX mode for formulas in graphs:在最近的一个版本(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(),您需要将字符串指定为元胞数组)。

                  【讨论】:

                  • Matlab 会在您的示例中抛出错误,因为传递给 plot 命令的向量长度不同。我想你是想画两条线,对吧?您需要在传递给绘图命令的矩阵中使用分号,以便 Matlab 知道它是两个独立的系列,即像这样:plot(t,sin(2*pi*[t ; t+0.25]));
                  【解决方案16】:

                  使用sim 命令直接从脚本(而不是以交互方式)执行 Simulink 模型。您可以执行诸如从工作区变量中获取参数之类的操作,并在循环中重复运行sim 以模拟某些内容,同时更改参数以查看行为如何变化,并使用您喜欢的任何图形命令绘制结果。比尝试以交互方式执行此操作要容易得多,并且在可视化结果时,它比 Simulink“示波器”块提供了更多的灵活性。 (虽然你不能用它在模拟运行时实时查看发生了什么)

                  要知道的一个非常重要的事情是simset 命令的DstWorkspaceSrcWorkspace 选项。这些控制“To Workspace”和“From Workspace”块获取和放置结果的位置。 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;
                  

                  【讨论】:

                    【解决方案17】:

                    带有[c,h]=contourclabel(c,h,'fontsize',fontsize) 的等高线图。我通常使用fontsize 参数来减小字体大小,这样数字就不会相互碰撞。这非常适合查看 2-D 函数的值,而无需处理 3D 图形。

                    【讨论】:

                      【解决方案18】:

                      匿名函数,原因如下:

                      1. 为一次性使用创建快速函数,例如 3x^2+2x+7。 (见下面的清单)这对于像 quadfminbnd 这样将函数作为参数的函数很有用。它在脚本(不以函数头开头的 .m 文件)中也很方便,因为与真正的函数不同,您不能包含子函数。
                      2. for closures -- 尽管匿名函数有一点限制,因为似乎没有办法在它们中进行赋值以改变状态。

                      % 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);
                      

                      【讨论】:

                      • 关于在脚本中使用的要点!
                      • 第 4 行末尾缺少一个 ')'。
                      • 感谢您的解释......我仍然对这种语言中所有这些荒谬的限制感到惊讶。
                      • Octave 允许内联分配,我认为这解决了您的第二点。
                      【解决方案19】:

                      赋值左侧的条件参数:

                      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);
                      

                      【讨论】:

                        【解决方案20】:

                        我喜欢使用函数句柄有很多原因。一方面,它们是我在 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);
                        

                        我只是觉得这样的小事很酷。

                        【讨论】:

                          【解决方案21】:

                          知道你的axis properties!你可以设置各种各样的东西来调整默认的绘图属性来做你想做的事情:

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

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

                          Linefigure 属性也很有用,但我发现自己最常使用轴属性。

                          【讨论】:

                            【解决方案22】:

                            Matlab 的bsxfunarrayfuncellfunstructfun 非常有趣,而且经常会保存一个循环。

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

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

                            不过,在应用程序的性能关键部分,您应该将这些函数与普通的 for 循环进行比较,因为通常循环仍然更快。

                            【讨论】:

                              【解决方案23】:

                              在使用 min、max、mean、diff、sum、any、all、...等聚合函数时,请严格指定维度。

                              例如这一行:

                              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
                              
                              >> 
                              

                              min 函数也是如此,它通常计算矩阵上列的最小值,直到矩阵仅包含一行。 - 然后它将返回该行的最小值,除非维度参数另有说明,并且可能会破坏您的应用程序。

                              我几乎可以向您保证,因此设置这些聚合函数的维度将在以后为您节省相当多的调试工作。

                              至少对我来说是这样。 :)

                              【讨论】:

                              • 这会失败,因为 matlab 不是 C/C++:你应该使用 a(1:end) 而不是 a(1:end-1)
                              • 这不会失败:diff 应用于大小为 n 的向量的结果大小为 n-1。
                              【解决方案24】:

                              我很惊讶,虽然人们提到了索引数组的逻辑数组方法,但没有人提到 find 命令。

                              例如如果 x 是一个 NxMxO 数组

                              x(x>20) 的工作原理是生成一个 NxMxO 逻辑数组并使用它来索引 x(如果您有大型数组并且正在寻找一个小的子集,这可能会很糟糕

                              x(find(x>20)) 的工作原理是生成满足 x>20 的 x 的索引列表(即 1xwhatever),并通过它索引 x。根据我的经验,应该更多地使用“find”。

                              更多我称之为“技巧”的东西

                              如果您不知道需要的大小,您可以增长/追加到数组和元胞数组,方法是使用 end + 1(也适用于更高的维度,只要切片的维度匹配 - 所以在这种情况下,您必须将 x 初始化为 [] 以外的其他值)。不适用于数字,但适用于事物的小型动态列表(或单元格数组),例如解析文件。

                              例如

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

                              另一个很多人不知道的想法是,它适用于任何 dim 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

                              我不记得是否有更好的方法。

                              【讨论】:

                              • 在这种情况下使用find 是个坏主意,因为它是多余的且速度较慢。就个人而言,我发现逻辑方法更清晰,因为它也避免了额外的混乱。
                              【解决方案25】:

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

                              -您可以使用“加载”功能加载整个数字 ascii 文件。这不是特别快,但可以快速完成原型设计工作(这不应该是 Matlab 的座右铭吗?)

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

                              【讨论】:

                                【解决方案26】:

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

                                1. 在 y=10 处画一条水平线:

                                  line(xlim, [10 10])

                                2. 在 x=5 处画垂直线:

                                  line([5 5], ylim)

                                【讨论】:

                                • 这并不总是有效。限制不会实时更新。在这种情况下,调用drawnow 将强制它更新它们。
                                【解决方案27】:

                                为了能够快速测试一个功能,我这样使用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;
                                

                                稍后,我添加了一个单元测试脚本来测试不同输入条件下的函数。

                                【讨论】:

                                  【解决方案28】:

                                  x=repmat([1:10],3,1); % 说,x 是一个示例数据数组

                                  l=x>=3; % l 是一个逻辑向量(1s/0s),用于突出显示数组中满足特定条件的那些元素。

                                  N=sum(sum(l));%N是满足给定条件的元素个数。

                                  干杯——快乐的脚本!

                                  【讨论】:

                                  • 如果 x 是 3D,那么您需要另一个 sum() 来计算 N。我会改用 N = sum(I(:));,它适用于任何维度。
                                  • 甚至numel(x&gt;=3)
                                  【解决方案29】:

                                  问“为什么”(有助于让我在凌晨 3 点从 Matlab 运行时失败调试恍惚状态中惊醒……)

                                  【讨论】:

                                    【解决方案30】:

                                    在运行在线算法时使用persistent(静态)变量。它可以加快贝叶斯机器学习等领域的代码速度,在这些领域中,模型会针对新样本进行迭代训练。例如,为了计算独立的对数似然,我最初从头开始计算对数似然,并通过将先前计算的对数似然和附加的对数似然相加来更新它。

                                    我没有给出更专业的机器学习问题,而是给出一个通用的在线平均代码,我采用from here

                                    function av = runningAverage(x)
                                    % The number of values entered so far - declared persistent.
                                    persistent n;
                                    % The sum of values entered so far - declared persistent.
                                    persistent sumOfX;
                                    if x == 'reset' % Initialise the persistent variables.
                                        n = 0;
                                        sumOfX = 0;
                                        av = 0;
                                    else % A data value has been added.
                                        n = n + 1;
                                        sumOfX = sumOfX + x;
                                        av = sumOfX / n; % Update the running average.
                                    end
                                    

                                    然后,调用将给出以下结果

                                    runningAverage('reset')
                                    ans = 0
                                    >> runningAverage(5)
                                    ans = 5
                                    >> runningAverage(10)
                                    ans = 7.5000
                                    >> runningAverage(3)
                                    ans = 6
                                    >> runningAverage('reset')
                                    ans = 0
                                    >> runningAverage(8)
                                    ans = 8
                                    

                                    【讨论】:

                                    • persistent 很危险,因为你不能直接设置内部状态,这意味着你不能正确测试。此外,这意味着您一次只能在一个地方使用该功能。例如,如果您想计算两个单独数量的运行平均值,则需要复制文件以分隔状态。
                                    • 确实,如果它没有帮助,我们应该避免使用它,因为它可能会导致难以注意到的意外问题。在我的问题中,我对几个变量进行了一些在线修改,因此它大大提高了代码的速度。应谨慎使用。
                                    猜你喜欢
                                    • 2010-10-25
                                    • 2010-09-12
                                    • 2010-10-06
                                    • 2010-09-06
                                    • 2010-09-05
                                    • 2010-11-29
                                    • 2010-10-08
                                    • 2011-07-18
                                    • 2010-09-13
                                    相关资源
                                    最近更新 更多