Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have a list which consists of elements each of which has a date in DateList format, a number and a flag (Y/N). e.g.

{{{2010, 11, 29, 16, 6,  0.}, 17.7, "N"}, 
 {{2010, 12, 2,  14, 38, 0.}, 11.9, "N"}, 
 {{2010, 12, 2,  20, 1,  0.},  8.2, "N"}}

I want to extract those elements which are dated within the last 90 days and also the same date range but one year previous (into two separate lists).

I presume the way to do this would be to define an Interval between the AbsoluteTimes of the ends of the ranges. But how do I filter the list on this basis: i.e. apply a filter (IntervalMemberQ) on the basis of the AbsoluteTime of the first element in each element in the list? I presume that it's something to do with pure functions but I can't get the syntax right.

share|improve this question
    
Related: (14482) –  Mr.Wizard Mar 17 '13 at 5:18

2 Answers 2

You have the logic right, so I'll show you an implementation based on it (I won't use IntervalMemberQ though, since it's simple enough without it).

To demonstrate, I'll slightly modify your input list to include dates from this year, since you said "within the last 90 days" (your dates are all from 2010, which means you must have a different reference point).

l = {{{2011, 12, 29, 16, 6, 0.}, 17.7, "N"}, 
     {{2012, 04, 29, 19, 6, 0.}, 17.7, "N"}, 
     {{2013, 5, 2, 14, 38, 0.}, 11.9,  "N"}, 
     {{2013, 2, 2, 20, 1, 0.}, 8.2, "Y"}};

selectElements[list_] := Module[{T = AbsoluteTime@DateList@Most@DateList[], T90d, T1y, T1y90d},
    {T90d, T1y, T1y90d} = T~DatePlus~# & /@ {-90, {-1, "Year"}, {{-1, "Year"}, {-90, "Day"}}};
    Select[list, Composition[T1y90d <= # <= T1y || T90d <= # <= T &, AbsoluteTime, First]] 
]

And now when you call this function on the input list, you get:

selectElements@l
(* {{{2011, 12, 29, 16, 6, 0.}, 17.7, "N"}, {{2013, 2, 2, 20, 1, 0.}, 8.2, "Y"}} *)

Based on the above, you can extend the definition of selectElements to select from within a custom date range as:

selectElements[list_, start_, end_] := Module[
    {s = AbsoluteTime@start, e = AbsoluteTime@end},
    Select[list, Composition[s <= # <= e &, AbsoluteTime, First]]
]

and you can use this as, say, selectElements[l, {2012,12,29}, {2013,2,23}].

share|improve this answer
    
That's great and is possibly the easiest for an absolute beginner like me to understand. Thanks for your advice. –  TimGJ Mar 18 '13 at 12:53
ClearAll[prvsNdys1a];
prvsNdys1[data_, numdays_: 90, basedate_: DateList[], offset_: {0, "Year"}] := 
 With[{dtrange = DateRange[DatePlus[Take[basedate, 3], offset],
 DatePlus[Take[basedate, 3], {offset, {-numdays + 1, "Day"}}], {-1,  "Day"}]},
 Select[data, MemberQ[dtrange, First[#]] &]];

or

ClearAll[prvsNdys1b];
prvsNdys1b[data_, numdays_: 90, basedate_: DateList[],  offset_: {0, "Year"}] := 
 With[{at1 = AbsoluteTime[DatePlus[Take[basedate, 3], offset]], 
      at2 = AbsoluteTime[ DatePlus[Take[basedate, 3], {offset, {-numdays + 1, "Day"}}]]},
  Select[data, (at2 <= AbsoluteTime[#[[1]]] <= at1 &)]];

Example data:

dates = With[{first = Take[DateList[], 3]},
   DateRange[first, DatePlus[first, {-5 365, "Day"}], {-1, "Day"}]];
datalst = Transpose@{Sort@dates, Range[5 365 + 1], 
    RandomChoice[{"Y", "N"}, {5 365 + 1}]};
datalst[[;; 4]]
(*{{{2008, 3, 18}, 1, "Y"}, {{2008, 3, 19}, 2, "Y"}, {{2008, 3, 20}, 3, "Y"}, 
  {{2008, 3, 21}, 4, "N"}} *)

Usage examples:

Last 5 days:

prvsNdys1a[datalst, 5]
(* {{{2013, 3, 13}, 1822, "Y"}, {{2013, 3, 14}, 1823, "Y"},
   {{2013, 3, 15}, 1824, "Y"}, {{2013, 3, 16}, 1825,  "N"}, 
   {{2013, 3, 17}, 1826, "N"}}*)

Last 5 days and the corresponding days in previous 2 months:

prvsNdys1a[datalst, 5, DateList[], {-#, "Month"}] & /@ {0, 1, 2}
(* {{{{2013, 3, 13}, 1822, "Y"}, {{2013, 3, 14}, 1823, "Y"}, 
    {{2013, 3, 15}, 1824, "Y"}, {{2013, 3, 16}, 1825,  "N"},
    {{2013, 3, 17}, 1826, "N"}}, 
   {{{2013, 2, 13}, 1794, "Y"}, {{2013, 2, 14}, 1795, "N"}, 
    {{2013, 2, 15}, 1796, "N"}, {{2013, 2, 16}, 1797, "N"},
    {{2013, 2, 17}, 1798,  "N"}}, 
   {{{2013, 1, 13}, 1763, "N"}, {{2013, 1, 14}, 1764, "Y"},
    {{2013, 1, 15}, 1765, "N"}, {{2013, 1, 16}, 1766, "N"},
    {{2013, 1, 17}, 1767, "Y"}}}  *)

Last 3 days and corresponding days in the previous 3 years:

prvsNdys1a[datalst, 3, DateList[], {-#, "Year"}] & /@ Range[0, 4]
(*{{{{2013, 3, 15}, 1824, "Y"}, {{2013, 3, 16}, 1825, "N"}, {{2013, 3, 17}, 1826,"N"}}, 
   {{{2012, 3, 15}, 1459, "Y"}, {{2012, 3, 16}, 1460, "Y"},{{2012, 3, 17}, 1461, "Y"}}, 
   {{{2011, 3, 15}, 1093, "Y"}, {{2011, 3, 16}, 1094, "N"},{{2011, 3, 17}, 1095, "N"}}, 
   {{{2010, 3, 15}, 728,  "N"}, {{2010, 3, 16}, 729, "Y"},{{2010, 3, 17}, 730, "Y"}}, 
   {{{2009, 3, 15}, 363, "N"}, {{2009, 3, 16}, 364, "Y"}, {{2009, 3, 17}, 365, "Y"}}}*)

Alternative methods using Pick or Cases instead of Select:

Using Pick:

ClearAll[prvsNdys2a];
prvsNdys2a[data_, numdays_: 90, basedate_: DateList[], offset_: {0, "Year"}] := 
 With[{dtrange = DateRange[DatePlus[Take[basedate, 3], offset],
 DatePlus[Take[basedate, 3], {offset, {-numdays + 1, "Day"}}], {-1, "Day"}]},
 Pick[data, MemberQ[dtrange, #] & /@ First /@ data]];

or

 ClearAll[prvsNdys2b];
 prvsNdys2b[data_, numdays_: 90, basedate_: DateList[], offset_: {0, "Year"}] := 
  With[{at1 =UnitStep[ AbsoluteTime[DatePlus[Take[basedate, 3], offset]] - 
       AbsoluteTime /@ First /@ data], 
       at2 = UnitStep[ AbsoluteTime /@ First /@ data - 
       AbsoluteTime[DatePlus[Take[basedate, 3], {offset, {-numdays + 1, "Day"}}]] ]},
  Pick[data, at1 at2, 1]];

Using Cases:

ClearAll[prvsNdys3a];
prvsNdys3a[data_, numdays_: 90, basedate_: DateList[], offset_: {0, "Year"}] := 
 With[{dtrange = DateRange[DatePlus[Take[basedate, 3], offset],
 DatePlus[Take[basedate, 3], {offset, {-numdays + 1, "Day"}}], {-1, "Day"}]},
 Cases[data, {Alternatives @@ dtrange, _, _}]];
share|improve this answer
    
Many thanks. Is there any saving in converting DateLists to AbsoluteTimes or is this done implicitly? –  TimGJ Mar 18 '13 at 12:59

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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