7
$\begingroup$

I'm not explaining it well, sorry.

There is a list[j] consisting of randomly selected number {a,b} elements.

For example,({j,4})

list[j]={{{2,1},{2,3},{1,2},{1,1}},
         {{3,1},{3,3},{4,2},{4,3}},
         {{1,1},{1,2},{5,2},{5,1}},
         {{4,2},{1,2},{3,3},{5,4}}}

I would like to change the above list to display the number of occurrences of {a,b}.

list[j] = {{{{2,1},1},{{2,3},1},{{1,2},1},{{1,1},1}},
           {{{3,1},1},{{3,3},1},{{4,2},1},{{4,3},1}},
           {{{1,1},2},{{1,2},2},{{5,2},1},{{5,1},1}},
           {{{4,2},2},{{1,2},3},{{3,3},2},{{5,4},1}}}

I break it down further. list[j] consists of the following.

list[1]={{2,1},{2,3},{1,2},{1,1}}
list[2]={{3,1},{3,3},{4,2},{4,3}}
list[3]={{1,1},{1,2},{5,2},{5,1}}
list[4]={{4,2},{1,2},{3,3},{5,4}}

Since list[1] and list[2] do not have the same combination in {a,b}, each is the first time,so there become

list[1] = {{2,1},1},{{2,3},1},{{1,2},1},{{1,1},1}}
list[2] = {{3,1},1},{{3,3},1},{{4,2},1},{{4,3},1}}

{1,1} and {1,2} of list[3] are the second time in list[j]

 list[3]={{1,1},2},{{1,2},2},{{5,2},1},{{5,1},1}}

{4,2} and {3,3} are 2 times, {1,2} the 3 times and {5,4} the 1st time in list[j].

 list[4]={{4,2},2},{{1,2},3},{{3,3},2},{{5,4},1}}

So the list [j] looks like this.

list[j] = {{{{2,1},1},{{2,3},1},{{1,2},1},{{1,1},1}},
           {{{3,1},1},{{3,3},1},{{4,2},1},{{4,3},1}},
           {{{1,1},2},{{1,2},2},{{5,2},1},{{5,1},1}},
           {{{4,2},2},{{1,2},3},{{3,3},2},{{5,4},1}}}

What would you suggest I do?

$\endgroup$
2
  • $\begingroup$ Thank you, it was a typo. $\endgroup$
    – hare
    Jul 13, 2023 at 10:22
  • $\begingroup$ Thanks everyone! $\endgroup$
    – hare
    Jul 27, 2023 at 17:41

7 Answers 7

5
$\begingroup$

Welcome to StackOverflow! The Tally command is pretty useful and does it automatically.

Simply do:

list = {{{2, 1}, {2, 3}, {1, 2}, {1, 1}}, {{3, 1}, {3, 3}, {4, 2}, {4,
      3}}, {{1, 1}, {1, 2}, {5, 2}, {5, 1}}, {{4, 2}, {1, 2}, {3, 
     3}, {5, 4}}};
flattenList = Flatten[list, 1]
talliedList = Tally[flattenList]

That will output you a list of coincidences next to the element repeated. You can create a function and call it over any list you may have:

enter image description here

Extra info

Also I think you are using wrongly the syntax of Wolfram language, as list[j] is equivalent of having a function called list and evaluating it at j. Then I imagine you are trying to describe a list of lists, so list[1] would be list[[1]], to get the first element of the list, list[[2]] to get the second list and so on. Rule of thumb: in Wolfram Mathematica f[x] <=> in math f(x)

This not may completely solve your issue, but you can try adding more information and steps next time.

P.D. You may want to detail some of your work in progress to get some help. That can help you to understand the problem better, and being able to simply sharing a simplified version of your trouble will make you a better programmer

$\endgroup$
1
  • $\begingroup$ Nice, but I don't think this is what the OP requires. (A 'running sum' of the occurrences rather than a list of 'coincidences'?). If the list is {a,b,a}, then {{a, 1}, {b, 1}, {a, 2}} rather than {{a, 2}, {b, 1}}? $\endgroup$
    – user1066
    Jul 14, 2023 at 7:34
5
$\begingroup$

Looks like we need a sort of "rolling" Count that then gets applied in a threaded way.

rowRules = FoldList[Merge[{##}, Total] &, Counts /@ list[j]]

And then

MapThread[
  ReplaceAll[#1, pair : {a_, b_} :> {a, b, {Lookup[#2, Key[pair]]}}] &, 
  {list[j], rowRules}]

[I assume there's a typo in your expected output]

$\endgroup$
5
$\begingroup$

For each j up to the length of list, count occurrences of {a, b} elements in list[j], up to and including the elements in list[j]. Preserve the order of {a, b} elements in list[j].

counts = Table[
  SortBy[
    Cases[Tally[Flatten[list[[1;;n]], 1]], {{a_,b_},c_}/; MemberQ[list[[n]],{a,b}]],
    First[#]/.PositionIndex[list[[n]]]&], {n, Length[list]}]
{
{{{2,1},1},{{2,3},1},{{1,2},1},{{1,1},1}},
{{{3,1},1},{{3,3},1},{{4,2},1},{{4,3},1}},
{{{1,1},2},{{1,2},2},{{5,2},1},{{5,1},1}},
{{{4,2},2},{{1,2},3},{{3,3},2},{{5,4},1}}}
$\endgroup$
1
  • $\begingroup$ Sorry, I noticed that I didn't send a reply. Your reply was very helpful for me! Thank you very much. $\endgroup$
    – hare
    Jul 27, 2023 at 17:39
5
$\begingroup$
ClearAll[accumulatedCounts]

accumulatedCounts = MapThread[KeyValueMap[List]@*KeyTake] @ 
  {Accumulate[KeyUnion[Counts /@ #, 0 &]], #} &;

Example:

inputlist = {{{2, 1}, {2, 3}, {1, 2}, {1, 1}}, 
             {{3, 1}, {3, 3}, {4, 2}, {4, 3}},
             {{1, 1}, {1, 2}, {5, 2}, {5, 1}}, 
             {{4, 2}, {1, 2}, {3, 3}, {5, 4}}};


result = accumulatedCounts @ inputlist
{{{{2, 1}, 1}, {{2, 3}, 1}, {{1, 2}, 1}, {{1, 1}, 1}},  
 {{{3, 1}, 1}, {{3, 3}, 1}, {{4, 2}, 1}, {{4, 3}, 1}},   
 {{{1, 1}, 2}, {{1, 2}, 2}, {{5, 2}, 1}, {{5, 1}, 1}},  
 {{{4, 2}, 2}, {{1, 2}, 3}, {{3, 3}, 2}, {{5, 4}, 1}}}
$\endgroup$
4
$\begingroup$

Not very efficient, but a solution using MapIndexed to count only to the occurrence of each element in list

With[{list2=Catenate@list},
  Partition[MapIndexed[{#,Count[list2[[;;#2[[1]]]],#1]}&, list2],4]
] 

(* {
    {{{2, 1}, 1}, {{2, 3}, 1}, {{1, 2}, 1}, {{1, 1}, 1}},
    {{{3, 1}, 1}, {{3, 3}, 1}, {{4, 2}, 1}, {{4, 3}, 1}}, 
    {{{1, 1}, 2}, {{1, 2}, 2}, {{5, 2}, 1}, {{5, 1}, 1}}, 
    {{{4, 2}, 2}, {{1, 2}, 3}, {{3, 3}, 2}, {{5, 4}, 1}}
   } *)

where

list={{{2,1},{2,3},{1,2},{1,1}},{{3,1},{3,3},{4,2},{4,3}},{{1,1},{1,2},
      {5,2},{5,1}},{{4,2},{1,2},{3,3},{5,4}}}

Simpler Example

lst={a,b,a,b,a,b,a,b,c,d,e,f}

MapIndexed[{#,Count[lst[[;;#2[[1]]]],#1]}&, lst]

(* {{a, 1}, {b, 1}, {a, 2}, {b, 2}, {a, 3}, {b, 3}, {a, 4}, {b, 4}, {c, 1}, 
    {d, 1}, {e, 1}, {f, 1}}
$\endgroup$
4
$\begingroup$
list =
  {{{2, 1}, {2, 3}, {1, 2}, {1, 1}},
   {{3, 1}, {3, 3}, {4, 2}, {4, 3}},
   {{1, 1}, {1, 2}, {5, 2}, {5, 1}},
   {{4, 2}, {1, 2}, {3, 3}, {5, 4}}};

Using c as association counter:

Module[{c = <|# -> 0 & /@ Union[Join @@ list]|>},
 Map[{#, c[#] += 1} &, list, {2}]]

gives

{{{{2, 1}, 1}, {{2, 3}, 1}, {{1, 2}, 1}, {{1, 1}, 1}},
 {{{3, 1}, 1}, {{3, 3}, 1}, {{4, 2}, 1}, {{4, 3}, 1}},
 {{{1, 1}, 2}, {{1, 2}, 2}, {{5, 2}, 1}, {{5, 1}, 1}},
 {{{4, 2}, 2}, {{1, 2}, 3}, {{3, 3}, 2}, {{5, 4}, 1}}}
$\endgroup$
2
$\begingroup$

A minor variation:

Using MapIndexed:

alist = {{{2, 1}, {2, 3}, {1, 2}, {1, 1}}, {{3, 1}, {3, 3}, {4, 
     2}, {4, 3}}, {{1, 1}, {1, 2}, {5, 2}, {5, 1}}, {{4, 2}, {1, 
     2}, {3, 3}, {5, 4}}, {{2, 1}, {2, 3}, {1, 2}, {4, 2}}};

d1 = First@Dimensions@alist

MapIndexed[{#1, Count[flist[[1 ;; (#2 - 1) . {d1, 1} + 1]], #1]} &,    alist, {2}] // MatrixForm

enter image description here


Using PositionIndex:

Using the same list as before but appending the first row to the end of the list.

alist = {{{2, 1}, {2, 3}, {1, 2}, {1, 1}}, {{3, 1}, {3, 3}, {4, 
     2}, {4, 3}}, {{1, 1}, {1, 2}, {5, 2}, {5, 1}}, {{4, 2}, {1, 
     2}, {3, 3}, {5, 4}}, {{2, 1}, {2, 3}, {1, 2}, {4, 2}}};


accCounts[k_List] := Module[{
   d1 = If[VectorQ[k], Length@k, #[[2]] &@Dimensions@k]
   , flist = Flatten[#, 1] &@k
   },
  vpi = Values@PositionIndex[flist];
  rvpi = Range@*Length /@ vpi;
  reps = Sort@Flatten[#, 1] &@
    MapThread[Thread[#1 -> #2] &, {vpi, rvpi}];
  rule = MapAt[{1, 1} + QuotientRemainder[# - 1, d1] &, reps, {All, 1}]
  ;
  If[VectorQ[k],
   MapIndexed[{#1, Sequence @@ (#2 /. reps)} &, clist]
   , MapIndexed[{#1, #2 /. rule} &, k, {2}]
   ]
  ]

Usage:

accCounts[alist]

blist = {{1, 2, 3}, {1, 2, 5}};
accCounts[blist]

clist = {4, 5, 7, 4, 1, 2, 1};
accCounts[clist]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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