I have some dates in a list, and I want to plot how many of those dates are Monday, Tuesday, etc.

I'm using this:

data = {...}
Tally[DayOfWeek /@ data]

and I get something like:

{{Tuesday, 533}, {Monday, 491}, {Sunday, 487}, {Saturday, 481}, 
 {Friday, 422}, {Thursday, 353}, {Wednesday, 371}}

I want to plot my list using BarChart, but I have two problems: The data is not sorted by day-of-the-week and all days may not appear in the data.

  • $\begingroup$ I found that this question is a duplicate and I am marking it as such. If anyone disagrees please comment and/or vote to reopen. $\endgroup$ – Mr.Wizard Jun 18 '13 at 22:57
  • 1
    $\begingroup$ @Mr.Wizard This is not about counting... OP wants to Sort the list by day of the week. This is not a duplicate, IMO. $\endgroup$ – rm -rf Jun 19 '13 at 5:10
  • $\begingroup$ @Mr.Wizard I agree with rm -rf; this mixes sorting with data tidying. $\endgroup$ – Pillsy Jun 19 '13 at 16:08
  • $\begingroup$ @Pillsy You can vote to reopen... $\endgroup$ – rm -rf Jun 19 '13 at 18:23
  • 1
    $\begingroup$ @Pillsy I don't know why but I didn't get notifications today. Anyway, I still believe this is a duplicate: the problem is to produce a tally of a certain set of objects, in order, including any zero counts. Sorting alone is not adequate to handle the zero cases as requested. The only difference between this question and my suggested duplicate is the use of day names rather than integers. $\endgroup$ – Mr.Wizard Jun 20 '13 at 0:34

I would do this with rule replacement. First, you want to have a list of the days of the week in the appropriate order:

days = DayName@{0, 0, #} & /@ Range[5, 11];

Then, you can take tallied results and turn them into a list of rules:

tallied = {{Tuesday, 533}, {Sunday, 487}, {Saturday, 481}, 
   {Friday, 422}, {Thursday, 353}, {Wednesday, 371}};

Note: I deleted the entry for Monday. From there, you can turn tallied into a list of rules, and add a default to insert 0 for missing days:

days /. Append[Rule @@@ tallied, Alternatives @@ days -> 0]
{487, 0, 533, 371, 353, 422, 481}

That's probably the simplest way if you might have missing days.

EDIT to add belisarius' expression for days.

Update for version 10.x

This becomes even easier in version 10, now that we have Associations and their supporting functions, especially the nifty PositionIndex. Keeping the definition for days from above, we have:

(* I don't like Mondays, and besides, I want to make sure the 0s wind up 
   in the right places. *)
In[1]:= data = DeleteCases[data, Monday];

In[2]:= counts = Join[<|Thread[days -> 0]|>, Counts[data]]
Out[2]= <|Sunday -> 487, Monday -> 0, Tuesday -> 533, Wednesday -> 371, 
         Thursday -> 353, Friday -> 422, Saturday -> 481|>

This form is convenient if you want a BarChart:

BarChart[counts, ChartLabels -> Automatic]

bar chart

Otherwise, you can get the ordered counts in a list even more easily:

In[3]:= Lookup[Counts[data], days, 0]
Out[3]= {487, 0, 533, 371, 353, 422, 481}
  • 4
    $\begingroup$ days = DayName@{0, 0, #} & /@ Range[5, 11] $\endgroup$ – Dr. belisarius Jun 18 '13 at 21:30
  • 2
    $\begingroup$ I love radix sort, such simplicity. $\endgroup$ – rcollyer Jun 19 '13 at 2:19

Mr. Wizard has shown today great method for counting. Look here.

days = {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
data = RandomChoice[days, 10^5];

It has advantages that are not useful here and in this case it is to slow, slower than others with Tally but seems to be the shortest :)

c[_] = 0;
++c[#] & /@ data;
c /@ days
{14342, 14170, 14278, 14215, 14299, 14115, 14581}
  • $\begingroup$ I am compelled to +1. :^) Two points: (1) this method will not be as efficient as the built-in Tally but sometimes that doesn't matter; (2) if you made this a "community wiki" post because you did not personally invent the method you need not. $\endgroup$ – Mr.Wizard Jun 18 '13 at 22:20
  • $\begingroup$ Purists could argue that Scan[++c[#] &, data] would be more appropriate here, but I often use /@ simply because it's shorter, and in many cases it makes little difference. $\endgroup$ – Mr.Wizard Jun 18 '13 at 22:33
  • $\begingroup$ @Mr.Wizard (Ad 1) Agree (Ad 2) Ok. But I have doubts. Maybe single "related" link could not be efficient in advertising this method. On the other hand I could have wait for Your response to this hypothetical link :) I feel that community wiki functionality is not well described in help etc. $\endgroup$ – Kuba Jun 18 '13 at 22:37
  • $\begingroup$ I removed the "community wiki" status because I think you deserve credit for the post. CW was (I believe) originally intended to make it easy for low-score users to contribute to a particular answer. An answer that is edited many times or by four or more different users will be automatically converted. By convention we use CW when a post is a gray-area answer such as with list-type questions. I have used it when I thought a question should be closed but I still wanted to answer. $\endgroup$ – Mr.Wizard Jun 18 '13 at 22:53
  • $\begingroup$ @Mr.Wizard Thank You for answer. It should be part of FAQ. Also a consensus about MMA output formatting should be there. The latter case seems to confuse new users. There is topic on meta, but it is not so easy to find if You expect this on FAQ or About. $\endgroup$ – Kuba Jun 18 '13 at 23:02

Since this question was reopened on the grounds that it should focus only on the sort and zero-fill of the Tally result rather than integrated ways to produce that result, here is an answer targeting specifically that. All starting with:

days = Array[DayOfWeek@{1, 5, #} &, 7];
tally = {{Tuesday, 533}, {Monday, 491}, {Sunday, 487}, {Saturday, 481},
         {Thursday, 353}, {Wednesday, 371}} (* Friday omitted *)

While it doesn't matter in the case of only seven elements, Pilly's method may be tweaked for better performance by using a level specification rather than Alternatives @@ days:

Replace[days, Append[Rule @@@ tally, _ -> 0], 1]
{487, 491, 533, 371, 353, 0, 481}

One can also use GatherBy:

Last /@ GatherBy[Join[{#,0}& /@ days, tally], First]
{{Sunday, 487}, {Monday, 491}, {Tuesday, 533}, {Wednesday, 371},
 {Thursday, 353}, {Friday, 0}, {Saturday, 481}}

Or Map:

Module[{r}, r@_ = 0; (r@# = #2) & @@@ tally; r /@ days]
{487, 491, 533, 371, 353, 0, 481}
Module[{r}, r@x_ := {x, 0}; (r@# = {##}) & @@@ tally; r /@ days]
{{Sunday, 487}, {Monday, 491}, {Tuesday, 533}, {Wednesday, 371},
 {Thursday, 353}, {Friday, 0}, {Saturday, 481}}

Or Sow and Reap:

Map[Tr, Reap[Sow[#2, #] & @@@ tally, days][[2]], 2]
{487, 491, 533, 371, 353, 0, 481}

Something similar to Pillsy:

days = {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
tallies = 
  {{Tuesday, 533}, {Monday, 491}, {Sunday, 487}, {Saturday, 481}, 
   {Friday, 422}, {Thursday, 353}, {Wednesday, 371}};    
SortBy[tallies, Position[days, #[[1]]] &]

results in:

{{Sunday, 487}, {Monday, 491}, {Tuesday, 533}, {Wednesday, 371}, 
 {Thursday, 353}, {Friday, 422}, {Saturday, 481}}
  • 1
    $\begingroup$ It will not work if some days are missing in data. He needs also {"day",0} information. $\endgroup$ – Kuba Jun 18 '13 at 21:47
  • $\begingroup$ Maybe I misunderstand, but if tallies = {{Tuesday, 533}, {Sunday, 487}, {Saturday, 481}, {Friday, 422}, {Thursday, 353}, {Wednesday, 371}} it still works for me. $\endgroup$ – chuy Jun 18 '13 at 21:52
  • $\begingroup$ It is an example after Tally[data]. If data contains all days. Tally will not give You for example {"Sunday",0}. It is not one time problem - Trollkemada: "... all days may not appear in the data." $\endgroup$ – Kuba Jun 18 '13 at 21:55
  • $\begingroup$ Oh I see what you mean now, this is a issue with Tally not returning {day,0} and not that the sorting does not work. $\endgroup$ – chuy Jun 18 '13 at 21:56
  • $\begingroup$ Yes, this is the problem :) $\endgroup$ – Kuba Jun 18 '13 at 21:57

A direct approach without Tally:

days = {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, 

data = RandomChoice[days, 10^5];

Map[{#, Count[data, #]} &, days]

results in:

{{Sunday, 14498}, {Monday, 14160}, {Tuesday, 14490}, {Wednesday, 14294},
 {Thursday, 14089}, {Friday, 14312}, {Saturday, 14157}}
  • $\begingroup$ This is less than optimal because the data must be scanned seven times rather than once. Whether that matters depends on the scale of the problem of course. $\endgroup$ – Mr.Wizard Jun 18 '13 at 22:15

Pillsy's method is one of the most efficient. However, I like using other methods too. Kuba already showed one, which is a manually incremented counter. The other is Sow and Reap.

days = Array[DayOfWeek@{1, 5, #} &, 7]
{Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}
data = RandomInteger[{1, 12}, {20, 3}];

Map[Tr, Reap[Sow[1, DayOfWeek /@ data], days][[2]], 2]
{3, 4, 1, 5, 3, 0, 4}

For completeness, though it had skipped my mind, this method may be fastest of all for general data (i.e. not a complied function for packed arrays):

Module[{t = Tally[Join[days, DayOfWeek /@ data]]}, --t[[All, 2]]; t]
{{Sunday,3}, {Monday,4}, {Tuesday,1}, {Wednesday,5}, {Thursday,3}, {Friday,0}, {Saturday,4}}

Credit to jVincent, though I know it has been used by others before (reference MathGroup), and this is my own flavor of it.

  • 2
    $\begingroup$ tut tut. answering and voting to close... :P $\endgroup$ – rm -rf Jun 19 '13 at 18:24
  • $\begingroup$ @rm-rf I didn't find/remember the duplicate until later. I also didn't remember the best method from that post which makes me sad. :-( $\endgroup$ – Mr.Wizard Jun 19 '13 at 18:39
  • $\begingroup$ But do you think it is a duplicate? Didn't seem so to me, but I leave this to you... I don't have much time (hence more answers on meta than main :P) $\endgroup$ – rm -rf Jun 19 '13 at 18:48
  • $\begingroup$ @rm-rf Sorry, I missed your reply. Other than day names instead of integers isn't it the same?: Produce a tally of a certain set of objects, in order, including any zero counts. Or have I misinterpreted something? $\endgroup$ – Mr.Wizard Jun 20 '13 at 0:23
  • $\begingroup$ I cast the 5th reopen vote just now, before I read your message. I don't think the question is about tallying either. It can be boiled down to "Write a function f that sorts {M, Su, W, F, Tu, Sa, Th} into canonical week order." OP already has a tallied list. He just wants to sort it in the right order. $\endgroup$ – rm -rf Jun 20 '13 at 0:29

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.