Skip to main content

algorithm - How can I extend BinCounts to work on Times and Dates?


I want to extend BinCounts to work on a list of times or dates, binning them by Day, Week, Hour of the Month, Day of the Year, etc... So given a list of times and a time bin spec, I need to calculate the bin counts.


One example might be if I have list of times I've been on Facebook and I want to to plot these times on a 2D Histogram where the Y axis is "MinuteOfTheDay" and the x axis is "DayOfTheWeek". Alpha can already do this (but not with arbitrary time bins)


enter image description here


To clarify my question I made a prototype:


d = RandomDates[1000];
Timing[TimeBinCounts[d, {"Day", "Week"}]]

gives the proper counts {157, 150, 136, 147, 149, 129, 132}, (e.g. Count[DayOfWeek /@ d, Monday] == 157). And queries like


Timing[TimeBinCounts[d, {"Month", "Year"}]]


work too, but I'm not sure how to handle an arbitrary time bin spec like "HoursOfTheWeek"


Timing[TimeBinCounts[d, {"Hour", "Week"}]]

Will not work...


<TimeBinCounts::badspec = "The time bin spec `1` is not acceptable.";
DaysOfTheWeek = {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
daysNum = Thread[Rule[DaysOfTheWeek, Range[7]]];
RandomDateList[] := {RandomInteger[{1800, 2100}], RandomInteger[{1, 12}], RandomInteger[{1, 28}], RandomInteger[{0, 23}], RandomInteger[{0, 59}], RandomInteger[{0, 59}]};

RandomDates[n_] := Table[RandomDateList[],{n}]

TimeSpecHelper[spec_] := Module[
{types, convert, result},
types = {"Second", "Minute", "Hour", "Day", "Week", "Month", "Year"};
convert = {60, 60, 24, 7, 4.345, 12};
result = Range[Times @@ Part[convert, Most[Range @@ Flatten[
First[Position[types, #]]& /@ spec]]]];
If[MatchQ[spec[[1]], "Second"|"Minute"|"Hour"],
result - 1,

result
]
]

TimeBinCounts[times_List, spec_] := Module[
{dayorder, bins, minDate, maxDate, order},

minDate = Min[times[[All, 1]]];
maxDate = Max[times[[All, 1]]];


order = Thread[Rule[{"Second", "Minute", "Hour", "Day", "Week", "Month", "Year"}, Range[7]]];
If[Greater @@ (spec /. order),
Message[TimeBinCounts::badspec, spec];
Return[$Failed];
];

Switch[spec,
{"Day","Week"} | "Day",
With[{days = DayOfWeek /@ times /. daysNum},
BinCounts[days, {1, 8, 1}]],

{"Hour", "Day"}|"Hour",
BinCounts[times[[All, 4]], {0, 24, 1}],
{"Day", "Month"},
BinCounts[times[[All, 3]], {1, 32, 1}],
{"Month", "Year"}|"Month",
BinCounts[times[[All, 2]], {1, 13, 1}],
"Year",
With[{interval = Range[minDate[[1]], maxDate[[1]]]},
Count[times[[All, 1]], #]& /@ interval
],

_,
order = Thread[Rule[{"Second", "Minute", "Hour", "Day", "Month", "Year"},
Range[6]]];
With[{interval = TimeSpecHelper[spec]},
Count[Mod[times[[All, 6 - (spec[[1]] /. order) + 1]], Length[interval]], #]& /@ TimeSpecHelper[spec]
]
]
]


Comments

Popular posts from this blog

list manipulation - Selecting multiple columns from a matrix?

Sample data: data = { {{2013, 1, 1}, 24.13, 167.67, 231.82}, {{2013, 1, 2}, 32.15, 170.92, 225.99}, {{2013, 1, 3}, 35.43, 172.68, 221.67}, {{2013, 1, 4}, 36.73, 173.05, 218.32}, {{2013, 1, 5}, 58.19, 165.96, 197.05}, {{2013, 1, 6}, 69.99, 163.50, 187.52}, {{2013, 1, 7}, 71.37, 154.21, 175.58}, {{2013, 1, 8}, 72.51, 149.66, 163.25}}; I want a DateListPlot with three graphs, so for a matrix formed by columns 1 and 2, one for columns 1 and 3, and 1 for columns 1 and 4. At the moment I'm using this code: data2 = Transpose[{data[[All, 1]], data[[All, 2]]}]; data3 = Transpose[{data[[All, 1]], data[[All, 3]]}]; data4 = Transpose[{data[[All, 1]], data[[All, 4]]}]; DateListPlot[{data2, data3, data4}, Joined -> True, Filling -> {3 -> {1}}] but I have a hunch that this can be done more efficiently. I don't like the Transpose s in particular. Any ideas? edit (for extra credit) What if I need to multiply the second column by 2, which in my solution is simp...

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...