# How to add a column to a table that counts the number of times the lines appear?

I am at a complete loss at this. I have a table with 2 columns and n lines. Some of the lines are repeated. What I want to do is add a new column that counts the number of repetitions of each line. Example

a={{1.1, 2.2},{1.1, 2.2}}


then I need something that will add a column {2,2} to the end of the table, resulting in

{{1.1, 2.2, 2},{1.1, 2.2, 2}}


Keeping in mind that I don't know in advance how many times a line will appear in the table. If it helps, the same line will always appear in subsequent positions Thanks

-
Ok, but how do I specify that the column to be inserted is the number of times that line is repeated in the table? – bernie Jun 10 '14 at 18:19
Tally[a] // Map[Flatten] – alancalvitti Dec 19 '14 at 15:27

## 8 Answers

Perhaps something like the following. Not very elegant but it should get the job done.

Generate some data for demonstration purposes...

SeedRandom[125];
data = Sort@RandomChoice[{1, 2}, {10, 3}]


Determine how many replicates there are.

reps = Join @@ Replace[Split[data], x_ :> ConstantArray[Length[x], Length[x]], {1}]


Join it all back together.

Transpose[Join[Transpose[data], {reps}]]

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

-
+1. This seems to be the fastest on my system for large arrays. – RunnyKine Jun 11 '14 at 8:06
a = {{1.1, 2.2}, {1.1, 2.2}, {x, b}, {c, d}, {x, b}, {1, 2}}

# /. (Rule[First@#, Append @@ #] & /@ Tally[#]) &[a]

(*
{{1.1, 2.2, 2}, {1.1, 2.2, 2}, {x, b, 2}, {c, d, 1}, {x, b, 2}, {1, 2,1}}
*)


For huge lists:

# /. Dispatch[Rule[First@#, Append @@ #] & /@ Tally[#]] &[a];


Will speed things up nicely.

Also quite fast on large lists:

Partition[Flatten[Riffle[#, Join @@ ConstantArray @@@ Tally[#][[All, {2, 2}]]]], 3] &@a


Unlike the above (where order of elements does not matter), this takes advantage of the OP assumptions of same elements appearing in sequence.

Even faster on large arrays:

Block[{tmp = ArrayPad[#, {{0, 0}, {0, 1}}], t = Tally[#]},
tmp[[All, 3]] = Join @@ ConstantArray @@@ t[[All, {2, 2}]];
tmp] &[data]

-
+1 - would be even faster if one could use - somehow - Join instead of Append. – eldo Jun 10 '14 at 22:34
These are clever, but in version 7 I'm not seeing the advantage over the more terse method I posted. e.g. given data = Join @@ RandomSample@Split@Sort@RandomInteger[500, {1000000, 2}]; both my code and your Block method take almost exactly two seconds on my machine. What kind of data are you using? -- Edit: in a fresh kernel yours is about 8 to 9 percent faster and both take a bit over one second. Still not worth the additional complexity in this particular test, IMHO. – Mr.Wizard Jun 11 '14 at 7:12
@Mr.Wizard: I went for the "some of the lines are repeated" slant. So, using your generator above, with 200 distinct (all lines end up repeated), I see about the same timings, with 500 (most lines end up repeated) I see yours ~20% slower, with 1000 distinct (resulting in ~1/3 of the lines having repeats) yours times ~40% slower, and with 3000 (~5% have repeats) it's ~55% slower. Both are plenty fast (along with Andy's answer. The others seem to blow up time-wise with larger problems), just fiddled with it as an exercise... – ciao Jun 11 '14 at 7:42

Using Tally and rules:

SeedRandom[125];
data = Sort@RandomChoice[{1, 2}, {10, 2}];
data /. (Tally[data] /. {{a_, b_}, d_} :> ({a, b} -> {a, b, d})) // TableForm
(* Out:
1   1   2
1   1   2
1   2   3
1   2   3
1   2   3
2   1   2
2   1   2
2   2   3
2   2   3
2   2   3 *)


This is probably not very fast. On the upside it works even if the lines are not consecutive.

-

Transpose and Count might be your friend

SeedRandom[125];
data = Sort@RandomChoice[{1, 2}, {10, 2}]
counted = Count[data, #] & /@ data
Transpose@Append[Transpose@data, counted]


with sample data

{{1, 1}, {1, 1}, {1, 2}, {1, 2}, {1, 2}, {2, 1}, {2, 1}, {2, 2}, {2,
2}, {2, 2}}


output is (TableForm'd for clarity)

1   1   2
1   1   2
1   2   3
1   2   3
1   2   3
2   1   2
2   1   2
2   2   3
2   2   3
2   2   3


EDIT Timing-wise, this starts to get increasingly sluggish at ~4,000 records, so the approaches that use ..Array or Dispatch may be preferable.

-

Yet another variation:

data = {{1, 2}, {0, 0}, {2, 0}, {0, 3}, {0, 3}, {3, 1}, {1, 3}, {1, 3}, {0, 1}, {0, 1}};

Join @@ (Append[##] ~ConstantArray~ #2 & @@@ Tally[data])

{{1, 2, 1}, {0, 0, 1}, {2, 0, 1}, {0, 3, 2}, {0, 3, 2},
{3, 1, 1}, {1, 3, 2}, {1, 3, 2}, {0, 1, 2}, {0, 1, 2}}


A refactoring of Andy's fast method that a bit shorter and also a little faster on my machine:

With[{reps = ConstantArray[#, #] & /@ Length /@ Split[data]},
Join[data\[Transpose], {Join @@ reps}]\[Transpose]
]


And a combination of Split and ArrayPad that is nearly as fast and shorter:

Join @@ (ArrayPad[#, {0, {0, 1}}, Length@#] & /@ Split[data])

-

Might as well join the fray.

list = RandomInteger[10, {100, 3}];
MapThread[Append, {list, list /. Rule @@@ Tally[list]}]

-

I'll just post this here as another option. This uses Reap and Sow.

rule = Dispatch[Reap[Sow[1, data], _, Rule[#1, Length@#2] &][[2]]];

Transpose @ Join[Transpose[data], {data /. rule}]


EDIT A relatively fast approach.

data = {{1, 2}, {0, 0}, {2, 0}, {0, 3}, {0, 3}, {3, 1}, {1, 3}, {1, 3}, {0, 1}, {0, 1}};

Transpose @ Join[Transpose[data], {data /. Dispatch[Rule @@@ Tally[data]]}]


{{1, 2, 1}, {0, 0, 1}, {2, 0, 1}, {0, 3, 2}, {0, 3, 2}, {3, 1, 1}, {1, 3, 2}, {1, 3, 2}, {0, 1, 2}, {0, 1, 2}}

-

Variations on @AndyRoss's method (for sorted data):

appndCnts = Join @@
(ConstantArray[Join[First@#, {Length@#}], Length@#] & /@ Split[#]) &;

appndCnts2 = Join @@
(ConstantArray[
PadRight[First@#, 1 + Length@First@#, Length@#], Length[#]
] & /@ Split[#]) &;

SeedRandom[125];
data = Sort@RandomChoice[{1, 2}, {10, 3}];

appndCnts@data
(* {{1, 1, 1, 1},
{1, 1, 2, 1},
{1, 2, 1, 2}, {1, 2, 1, 2},
{2, 1, 1, 1},
{2, 1, 2, 1},
{2, 2, 1, 1},
{2, 2, 2, 3}, {2, 2, 2, 3}, {2, 2, 2, 3}}*)

appndCnts[data] == appndCnts2[data]
(* True *)

-