Skip to main content
Added a (much) faster solution
Source Link
Leonid Shifrin
  • 114.9k
  • 16
  • 333
  • 424

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts. You can add a second definition to your original myFun, which would take a list of tracked elements as a second parameter, as follows:

ClearAll[myFun];
myFun[sublist_] := SortBy[Tally[sublist], First]
myFun[sublist_, elems_] :=
  Replace[myFun[sublist~Join~elems], 
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1];

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

EDIT

Since the performance issue came up, I have to mention that the above solution is suboptimal, but not due to Tally, but rather due to the use of Alternatives @@ elems, which makes its complexity to be O(Length[elems] * Length[sublist]). Here is a much faster one:

ClearAll[myFun1,leonid1];
myFun1[sublist_, elems_] :=
   Module[{tallyT = Transpose@Tally[elems ~ Join ~ sublist], 
      range = Range[Length[elems]]},
      tallyT[[2, range ]] = tallyT[[2, range ]] - 1;
      SortBy[Transpose[tallyT], First]
   ];
leonid1[dat_, bins_] := myFun1[#, bins] & /@ dat

from what I could see, it is the fastest so far. I save big because I put elems in front, and so, since Tally does its counts in the order it meets the elements, I know precisely the positions of elements where I need to adjust the counts. And I sort the result afterwards.

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts. You can add a second definition to your original myFun, which would take a list of tracked elements as a second parameter, as follows:

ClearAll[myFun];
myFun[sublist_] := SortBy[Tally[sublist], First]
myFun[sublist_, elems_] :=
  Replace[myFun[sublist~Join~elems], 
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1];

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts. You can add a second definition to your original myFun, which would take a list of tracked elements as a second parameter, as follows:

ClearAll[myFun];
myFun[sublist_] := SortBy[Tally[sublist], First]
myFun[sublist_, elems_] :=
  Replace[myFun[sublist~Join~elems], 
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1];

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

EDIT

Since the performance issue came up, I have to mention that the above solution is suboptimal, but not due to Tally, but rather due to the use of Alternatives @@ elems, which makes its complexity to be O(Length[elems] * Length[sublist]). Here is a much faster one:

ClearAll[myFun1,leonid1];
myFun1[sublist_, elems_] :=
   Module[{tallyT = Transpose@Tally[elems ~ Join ~ sublist], 
      range = Range[Length[elems]]},
      tallyT[[2, range ]] = tallyT[[2, range ]] - 1;
      SortBy[Transpose[tallyT], First]
   ];
leonid1[dat_, bins_] := myFun1[#, bins] & /@ dat

from what I could see, it is the fastest so far. I save big because I put elems in front, and so, since Tally does its counts in the order it meets the elements, I know precisely the positions of elements where I need to adjust the counts. And I sort the result afterwards.

refactored the code to minimally change the original OP's approach
Source Link
Leonid Shifrin
  • 114.9k
  • 16
  • 333
  • 424

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts. You can add a second definition to your original myFun, which would take a list of tracked elements as a second parameter, as follows:

ClearAll[myFun];
myFun[sublist_] := SortBy[Tally[sublist], First]
myFun[sublist_, elems_] :=
  Replace[SortBy[Tally[sublist~Join~elems]Replace[myFun[sublist~Join~elems], First],
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1]1];

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts:

myFun[sublist_, elems_] :=
  Replace[SortBy[Tally[sublist~Join~elems], First],
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1]

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts. You can add a second definition to your original myFun, which would take a list of tracked elements as a second parameter, as follows:

ClearAll[myFun];
myFun[sublist_] := SortBy[Tally[sublist], First]
myFun[sublist_, elems_] :=
  Replace[myFun[sublist~Join~elems], 
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1];

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.

Source Link
Leonid Shifrin
  • 114.9k
  • 16
  • 333
  • 424

You can still use your original strategy, just add a list of unique elements you wish to track, to your sublists, and then adjust the counts:

myFun[sublist_, elems_] :=
  Replace[SortBy[Tally[sublist~Join~elems], First],
     {el : Alternatives @@ elems, n_} :> {el, n - 1}, 1]

Then,

myFun[#, {1, 2, 3, 4}] & /@ myData

produces the result you desire.