0
$\begingroup$

See the example below. Is there any way to make this execute much faster and still get the same output ? I have read Conditionals slower than operators? but it didn't really help me as I can't seem to apply these methods to my problem. I have to execute about 100 of these operations in succession and each one can't take about a second to execute as running the algorithm would take way too long for the end user.

CurrentEquipID = 40

40

AbsoluteTiming[
 Table[If[HTimeModelSelection[CurrentEquipID][[i]] == 
    0, (1 - HOperatingEfficiency[CurrentEquipID][[i]])*
    HUtilizedTime[CurrentEquipID][[i]], 
   Flatten[ConstantArray[
      Select[EquipParams, #[[colEquipID]] == CurrentEquipID &][[;; , 
        colOperatingDelayTime]], 20]][[i]]], {i, 1, 20}]]

{1.851185, {744.6512332, 744.6512332, 746.713979, 744.6512332, 
  744.6512332, 744.6512332, 746.713979, 744.6512332, 744.6512332, 
  744.6512332, 746.713979, 744.6512332, 744.6512332, 744.6512332, 
  746.713979, 744.6512332, 744.6512332, 744.6512332, 746.713979, 
  744.6512332}}
$\endgroup$
3
  • 2
    $\begingroup$ I'm not going to try to work out exactly what your incomplete code is doing but Select inside a loop may be slow. Consider creating a hash table for each value (or position) rather than finding it with Select. $\endgroup$
    – Mr.Wizard
    Commented Jun 11, 2013 at 13:05
  • $\begingroup$ @Mr.Wizard its worse than that, that entire branch is redundant, and should be excised. See my answer. $\endgroup$
    – rcollyer
    Commented Jun 11, 2013 at 13:40
  • 1
    $\begingroup$ Considering how common an issue this is, I'm voting to close in favor of a comprehensive question that you should read. $\endgroup$
    – rcollyer
    Commented Jun 11, 2013 at 13:48

2 Answers 2

4
$\begingroup$

I was going to leave a comment, but without seeing the full code, I think it can be improved drastically.

At fault is that you are performing the same search every time through the loop. (If has the attribute HoldRest, so the branches are not executed unless they are used which means the search is re-executed every through.) So, at a minimum move the Select statement outside of the loop.

 currentEP = Select[EquipParams, #[[colEquipID]] == CurrentEquipID &][[;; , 
    colOperatingDelayTime]];

Additionally, your use of ConstantArray[..., 20][[i]] is redundant, and should be eliminated in its entirety. Replace it with currentEP. Lastly, using Table to index a List is inelegant; there are better ways. Consider this use of MapThread:

MapThread[
 If[ #1 == 0, (1 - #2) #3, Evaluate@Flatten[currentEP]]&,
 {
  HTimeModelSelection[CurrentEquipID],
  HOperatingEfficiency[CurrentEquipID],
  HUtilizedTime[CurrentEquipID]
 }
]

The nice thing about this construct is you do not need to know what iteration your on, simplifying your code. Also, if the lists change in size, you do not need to change the code. A caveat is that they must all be the same size, or MapThread will complain, loudly. But, there are other methods ...

$\endgroup$
3
  • $\begingroup$ I think that Evaluate@Flatten@currentEP should be a real number -- the OP extracts part i from the flattened list. Probably Select returns just one element, but who knows. $\endgroup$
    – Michael E2
    Commented Jun 11, 2013 at 16:31
  • $\begingroup$ @MichaelE2 you are likely right, but I was not assuming anything, particularly with Flatten used after the OP extracts the info from the list. But, Evaluate could be removed, as I believe Flatten is very fast, and the list is very short. $\endgroup$
    – rcollyer
    Commented Jun 11, 2013 at 16:38
  • $\begingroup$ Anyway, I posted alternatives, but I think your approach is easier to understand. Unitize, UnitStep, etc. and vectorized arithmetic seems a bit harder to me. $\endgroup$
    – Michael E2
    Commented Jun 11, 2013 at 19:25
1
$\begingroup$

Without knowing much about the data, it seems likely that it consists of numbers, and the the times and efficiencies are positive real numbers. Further I have to guess that

Select[EquipParams, #[[colEquipID]] == CurrentEquipID &][[;; , colOperatingDelayTime]]

returns a list consisting of a single number; otherwise, I cannot see how one would get predictable results picking the i-th element in the flattened array.

Here is some made-up data, on which your function works (i.e. runs without error and returns a list of real numbers):

nEquip = 10000;
numEquipmentStats = 100;
  colEquipID = 1; (* index *)
  colOperatingDelayTime = 3;  (* random index *)
CurrentEquipID = 40;
HTimeModelSelection[CurrentEquipID] = RandomInteger[{0, 2}, nEquip];
HOperatingEfficiency[CurrentEquipID] = RandomReal[1, nEquip];
HUtilizedTime[CurrentEquipID] = RandomReal[1, nEquip];
EquipParams = Transpose @ Join[{Range[nEquip]}, RandomReal[1, {numEquipmentStats, nEquip}]];

Your function takes 0.136795 sec. (Perhaps the slowness your function has to do with the functions HTimeModelSelection, HOperatingEfficiency, or HUtilizedTime -- your code calls them repeatedly on the same input -- something to avoid if your functions take an appreciable amount of time to evaluate.)

If the data in this calculation, except HTimeModelSelection, are positive numbers, then the following will be fast.

AbsoluteTiming[
 Unitize[HTimeModelSelection[CurrentEquipID]] (1. - 
       HOperatingEfficiency[CurrentEquipID]) HUtilizedTime[
      CurrentEquipID] /. 
    0 -> Select[EquipParams,
          #[[colEquipID]] == CurrentEquipID &, 1][[1, colOperatingDelayTime]];]
{0.002531, Null}

If the data is not all positive numbers, then here is a variation that works:

Transpose[{
     N@Unitize[HTimeModelSelection[CurrentEquipID]],
     (1. - HOperatingEfficiency[CurrentEquipID]) HUtilizedTime[CurrentEquipID]
    }] /. 
   {{0., _} -> Select[EquipParams,
                      #[[colEquipID]] == CurrentEquipID &, 1][[1, colOperatingDelayTime]],
    {1., x_} :> x};   // AbsoluteTiming
{0.006147, Null}

If that's not fast enough, then perhaps compiling will help:

cf = Compile[{{model, _Real, 1}, {eff, _Real, 1}, {time, _Real, 1}, {delay, _Real}},
   If[#[[1]] == 0., #[[2]], delay] & /@ Transpose[{model, (1. - eff) time}]];

cf[Unitize[HTimeModelSelection[CurrentEquipID]], 
   HOperatingEfficiency[CurrentEquipID],
   HUtilizedTime[CurrentEquipID],
   Select[EquipParams,
     #[[colEquipID]] == CurrentEquipID &, 1][[1, colOperatingDelayTime]]]; // AbsoluteTiming
{0.001194, Null}
$\endgroup$
3
  • $\begingroup$ You've got the order wrong, the ith element of ConstantArray[...] is picked then it is flattened. So, consistent results can be acheived. Oh, and +1 for pointing out that the OPs functions may be at fault, also. $\endgroup$
    – rcollyer
    Commented Jun 11, 2013 at 19:46
  • $\begingroup$ @rcollyer Aren't the [[;;, colOperatingDelayTime]] elements picked, ConstantArrayed, and flattened; and then the i-th element is picked? Seems like we're getting who-knows-which delay time, unless there's just one or they're all the same. $\endgroup$
    – Michael E2
    Commented Jun 11, 2013 at 20:49
  • $\begingroup$ You were right, I was reading the closing ] of If as the closing ] of Flatten. But, a lot of duplication ... $\endgroup$
    – rcollyer
    Commented Jun 11, 2013 at 20:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.