How to create a histogram of annual events, such as cherry blossom time

7

3

I have calculated blooming and ripening times for a specific variety of plant, based on actual temperature data from over 40 years. I would like to create two histograms on a single axis; one for blossoming and one for ripening. Surely there is a more straightforward way than converting to integer dates and adjusting for leap years…

Here is some actual data (years have been removed) in the form of {month, day}:

{
  {4, 3}, {4, 22}, {4, 15}, {4, 2}, {4, 18}, 
  {4, 20}, {4, 12}, {3, 30}, {4, 4}, {4, 24}, 
  {4, 26}, {3, 4}, {4, 26}, {4, 13}, {5, 1}, 
  {4, 4}, {4, 8}, {4, 18}, {4, 9}, {4, 19}, 
  {4, 10}, {4, 20}, {4, 3}, {4,4}, {3, 21}, 
  {4, 19}, {4, 15}, {4, 17}, {4, 9}, {4, 17}, 
  {4, 9}, {4, 8}, {4, 23}, {4, 17}, {4, 1}, 
  {4, 10}, {4, 15}, {4, 15}, {4, 11}, {4, 15}, 
  {4, 19}, {4, 22}, {4, 11}, {4, 4}, {4, 12}, 
  {3, 27}, {3, 24}, {4, 26}, {3, 28}, {4, 16}
}

R. Peter DeLong

Posted 2012-02-26T01:32:31.373

Reputation: 435

Please include an example of the output you desire. – Mr.Wizard – 2012-02-26T01:49:05.300

For example, there is a question right below yours that includes how to create this: http://i.stack.imgur.com/r9uMh.png

– Mr.Wizard – 2012-02-26T01:51:52.270

Are those values {month, day}? – Brett Champion – 2012-02-26T02:41:07.483

Wow. Thanks for the quick response… Yes, the values are {month,day}. The desired output is graphics - a histogram with two humps for each plant variety - one hump for the observed or calculated distribution of blooming dates, and the other for ripening dates. The two distributions is not the problem - it's the conversion of dates in Mathematica format to integers so that different years can be readily compared. I don't want to turn this into a calendar conversion problem accounting for leap years, etc. I would just like an x axis labeled with the date in a way that will be easy to read. – R. Peter DeLong – 2012-02-26T02:52:44.660

Maybe I should just go ahead and write a function to map: {1,x]-> x; {2,x}->31+x; {3,x}->59+x; etc. I'm just surprised that there isn't already such a function in Mathematica. – R. Peter DeLong – 2012-02-26T03:03:24.103

Or maybe I'm surprised that Mathematica doesn't accept times as an acceptable input to a histogram. I could add a false year to the data to make it look contemporaneous, if that is the problem. – R. Peter DeLong – 2012-02-26T03:09:07.447

Answers

11

Here are two approaches.

We'll create a second dataset by shifting the given data by two months:

blossom = {{4, 3}, {4, 22}, {4, 15}, {4, 2}, {4, 18}, {4, 20}, {4, 
    12}, {3, 30}, {4, 4}, {4, 24}, {4, 26}, {3, 4}, {4, 26}, {4, 
    13}, {5, 1}, {4, 4}, {4, 8}, {4, 18}, {4, 9}, {4, 19}, {4, 
    10}, {4, 20}, {4, 3}, {4, 4}, {3, 21}, {4, 19}, {4, 15}, {4, 
    17}, {4, 9}, {4, 17}, {4, 9}, {4, 8}, {4, 23}, {4, 17}, {4, 
    1}, {4, 10}, {4, 15}, {4, 15}, {4, 11}, {4, 15}, {4, 19}, {4, 
    22}, {4, 11}, {4, 4}, {4, 12}, {3, 27}, {3, 24}, {4, 26}, {3, 
    28}, {4, 16}};
ripen = TranslationTransform[{2, 0}][blossom];    

The first method converts the {month, day} into the number of the day in the year (1 for January 1st, 32 for February 1st, etc...) and creates a histogram from that.

DayOfYear[{m_, d_}] := 
 First[DateDifference[{2011, 12, 31}, {2012, m, d}, "Day"]]

{DayOfYear[{1, 1}], DayOfYear[{2, 1}], DayOfYear[{3, 1}]}
{1, 32, 61}
Histogram[{DayOfYear /@ blossom, DayOfYear /@ ripen}, 20]

enter image description here


The second approach is more involved. We convert the {month, day} values into absolute times, and then use HistogramList on the combined datasets to get bins and counts without yet constructing the graphic. We then create a corresponding DateListPlot of the data, for the sole purpose of getting access to how it creates date axes. Finally we combine the ticks from the DateListPlot with an actual Histogram, reusing the bins but recalculating the bins for the different datasets, to get the final graphic.

MonthDayToTime[{m_, d_}] := AbsoluteTime[{2012, m, d}]

blossomtimes = MonthDayToTime /@ blossom;
ripentimes = MonthDayToTime /@ ripen;

{bins, counts} = HistogramList[Join[blossomtimes, ripentimes], 20]

points = Transpose[{Riffle[bins, bins], ArrayPad[Riffle[counts, counts], 1]}];
dateplot = DateListPlot[points, Frame -> False, Axes -> True, Joined -> True]

Show[Histogram[{blossomtimes, ripentimes}, {bins}], Options[dateplot, Ticks]]

enter image description here

Brett Champion

Posted 2012-02-26T01:32:31.373

Reputation: 19 284

Thanks Brett! I'm still surprised that the functions DayOfYear[ ] and its inverse are not built in to Mathematica. I especially appreciate your second suggestion and the hints about labeling the x axis. I'll use 2011 as my fake year since it is not a leap year. – R. Peter DeLong – 2012-02-27T00:06:33.600

Great answer, I never would have known to pull the ticks options from the DateListPlot, very smart, I would have spent forever redoing the x-axis ticks manually. – s0rce – 2012-02-27T03:12:01.350

You could also use DaysBetween from the Calendar package to implement DayOfYear.

– Verbeia – 2012-03-05T21:20:20.413

5

DateHistogram was added in version 10.2, and uses date-specific bins and ticks.

I'll use the same {month, day} data as my other example, but instead of transforming the dates ahead of time, I can use DateFunction to provide the interpretation automatically.

blossom = {{4, 3}, {4, 22}, {4, 15}, {4, 2}, {4, 18}, {4, 20}, {4, 
    12}, {3, 30}, {4, 4}, {4, 24}, {4, 26}, {3, 4}, {4, 26}, {4, 
    13}, {5, 1}, {4, 4}, {4, 8}, {4, 18}, {4, 9}, {4, 19}, {4, 
    10}, {4, 20}, {4, 3}, {4, 4}, {3, 21}, {4, 19}, {4, 15}, {4, 
    17}, {4, 9}, {4, 17}, {4, 9}, {4, 8}, {4, 23}, {4, 17}, {4, 
    1}, {4, 10}, {4, 15}, {4, 15}, {4, 11}, {4, 15}, {4, 19}, {4, 
    22}, {4, 11}, {4, 4}, {4, 12}, {3, 27}, {3, 24}, {4, 26}, {3, 
    28}, {4, 16}};
ripen = TranslationTransform[{2, 0}][blossom]; 

Weekly bins

DateHistogram[{blossom, ripen}, "Week", 
    DateFunction -> (DateObject[Prepend[#, 2015]] &)]

enter image description here

Daily bins

DateHistogram[{blossom, ripen}, "Day", 
    DateFunction -> (DateObject[Prepend[#, 2015]] &)]

enter image description here

Brett Champion

Posted 2012-02-26T01:32:31.373

Reputation: 19 284

+1 for introducing me to a new function, even if I don't have it in 10.1. Something I wonder about is that you are giving every day a year of 2015, whereas the OP wrote: "based on actual temperature data from over 40 years" and "a more straightforward way than converting to integer dates and adjusting for leap years" -- I imagine ideally this should be plotted in sidereal time? – Mr.Wizard – 2016-02-06T10:51:59.923