3
$\begingroup$

Supposed that:

list = {2, 3, 5, 7, 9};
sublist = {{3, 3, 5}, {2, 2, 2, 7, 9}};

I want to change each sublist in sublist as following:

  1. Find the positions of elements in sublist in list.So,
{3, 3, 5} -> {2, 2, 3} and {2, 2, 2, 7, 9} -> {1, 1, 1, 4, 5}.
sublist1 = {{2 ,2, 3}, {1, 1, 1, 4, 5}};

2,Change each sublist in sublist1 to sparsearray:

{2, 2, 3} -> {0, 2, 1, 0, 0} and {1, 1, 1, 4, 5} -> {3, 0, 0, 1, 1}.
sublist2 = {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}};

How to do it?

$\endgroup$
  • $\begingroup$ What should be the length of each last sublist? The same as list's? $\endgroup$ – Kuba Sep 15 '13 at 22:40
  • $\begingroup$ Yes.Each sublist's length is the same as list. $\endgroup$ – incognito007 Sep 15 '13 at 22:45
4
$\begingroup$

It's perhaps simpler to use rule replacements and the efficient Tally to get the same answer than Position (which is inefficient):

list /. Join[Rule @@@ Tally@#, {_Integer -> 0}] & /@ sublist
(* {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}} *)
$\endgroup$
  • $\begingroup$ I must say I like it :P but also I think that this will not be the fastest solution for every variation of initial list/sublist. I suppose more answers will appear and someone will make some comparisons :p $\endgroup$ – Kuba Sep 15 '13 at 23:12
  • $\begingroup$ @Kuba It probably won't be the fastest, but it should be reasonably efficient, since it's not walking through list multiple times. $\endgroup$ – rm -rf Sep 15 '13 at 23:15
3
$\begingroup$

new - all in one.

Map[Function[x, Count[x, #] & /@ list], sublist]
{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}

old - step by step.

1)

sublist1 = Map[Position[list, #][[1, 1]] &, sublist, {2}]
{{2, 2, 3}, {1, 1, 1, 4, 5}}

2)

sublist2 = SparseArray[Rule @@@ Tally[#], Length@list] & /@ sublist1
{SparseArray[<2>, {5}], SparseArray[<3>, {5}]} 
Normal /@ sublist2
{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}
$\endgroup$
  • $\begingroup$ f[list_List, subList_List] := Map[Position[list, #] &, sublist, {Infinity}];? $\endgroup$ – Dr. belisarius Sep 15 '13 at 22:41
  • $\begingroup$ @belisarius out= {{3, 3, 5}, {2, 2, 2, 7, 9}} something is not ok. $\endgroup$ – Kuba Sep 15 '13 at 22:43
  • $\begingroup$ Might be more efficient if you do the tally first, since position is generally an inefficient operation: Composition[Normal, SparseArray[#, Length@list]&, Position[list, #][[1, 1]] -> #2 & @@@ # &, Tally] /@ sublist $\endgroup$ – rm -rf Sep 15 '13 at 22:54
  • $\begingroup$ @rm-rf Thanks for the remark. I've got even shorter code now :) $\endgroup$ – Kuba Sep 15 '13 at 22:58
  • $\begingroup$ @rm-rf Yes yes, I know, short but not effective with very sparse arrays. $\endgroup$ – Kuba Sep 15 '13 at 23:02
3
$\begingroup$

Here's another one-liner. Maybe not the fastest, but the most compact yet. I think it also makes it easier to understand what's being done.

Outer[Count,sublist,list,1]

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}}
$\endgroup$
  • $\begingroup$ My apologies but I must condemn this method. I am well known for my fixation on terse code but I cannot excuse this; it is exactly the kind of application that I lamented in Why is there no PositionFunction in Mathematica?. (I mean no offense to you by this; it's nice clean code but I consider it a "siren song" that new users must be warned against.) $\endgroup$ – Mr.Wizard Sep 16 '13 at 7:18
  • $\begingroup$ @Mr.Wizard I didn't say it was efficient, but I do think it's the clearest statement yet of what is to be done. Perhaps its best use would be in a comment, along with something like "that's what we need, and here's how we get it efficiently." $\endgroup$ – Ray Koopman Sep 16 '13 at 12:18
  • $\begingroup$ I agree that it is the clearest code. I think it deserves more than a comment. (You'll notice that I did not down-vote this answer.) I suppose I was overly harsh yesterday, but at the same time I think there is a genuine need for a warning with such things. I think what I'll do, if I have time, is provide timings for the methods posted; then people can see for themselves how these behave without me unfairly singling out your method for criticism. $\endgroup$ – Mr.Wizard Sep 16 '13 at 16:58
2
$\begingroup$

I presume your starting list will never contain duplicates. Therefore:

1.

sublist1 = sublist /. MapIndexed[#1 -> First[#2] &, list]
(* {{2, 2, 3}, {1, 1, 1, 4, 5}} *)

2.

sublist2 = BinCounts[#, {1, 6, 1}] & /@ sublist1
(* {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}} *)
$\endgroup$
  • $\begingroup$ BinCounts is/was quite slow in v7; how does it compare to Tally in v9? $\endgroup$ – Mr.Wizard Sep 16 '13 at 2:43
  • $\begingroup$ @Mr.Wizard been offline for a few days. In my experience BinCounts is usually slow -- I usually look for alternatives if speed is important -- but offered it here as an alternative. Whether it is viable depends on the real data to be used. $\endgroup$ – Mike Honeychurch Sep 20 '13 at 23:15
2
$\begingroup$

Also assuming list is duplicate free I propose this:

sparse[main_][sub_] := Tally[main ~Join~ sub][[All, 2]] - 1

Now:

sparse[list] /@ sublist
{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}

This works by "priming" Tally with the elements of the main list to get the order desired, then subtracting one to correct the totals. SubValues notation is used.


I like the style of the code above but this is slightly faster:

sparse2[main_, sub_] := (Tally[main ~Join~ #][[All, 2]] & /@ sub) - 1

sparse2[list, sublist]
{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}
$\endgroup$
1
$\begingroup$

Just another approach:

Function[u, 
  Reap[Sow[1, #] & /@ u, list, Total@#2 &][[2]] /. {{} -> 0, {x_} :> 
     x}] /@ sublist
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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