2
$\begingroup$

I have list of rules from where a DateListStepPlot can be created to visualize the problem:

dateToPopulationRules = {DateObject[{2019, 1, 1}] -> 5, DateObject[{2019, 2, 1}] -> 10, DateObject[{2019, 2, 13}] -> 6, DateObject[{2019, 4, 4}] -> 1};
DateListStepPlot[List @@@ dateToPopulationRules, PlotMarkers -> Automatic, FrameLabel -> {None, "Population"}]

enter image description here

I want to create an efficient function for population using a huge instance of dateToPopulationRules. Here's my poor implementation:

Clear[population];
population[dateObj_] := 0;
Table[
  With[{dateLim = First@dpr, pop = Last@dpr},
   population[dateObj_] := Condition[pop, dateObj >= dateLim]
   ]
  , {dpr, dateToPopulationRules}];

An example showing how the population function works is below. You may want to see DownValues@population in case that helps understanding my code.

In[11]:= population[DateObject[{2019, 3, 1}]]
Out[11]= 5

Although this approach is effective I don't think is the most efficient. I would like to see another solution using a Nearest function or other function that can handle large data sets efficiently.

$\endgroup$
2
  • 4
    $\begingroup$ If you want it to be efficient, it's probably best to convert the dates to AbsoluteTime. Simple numbers will beat date objects in performance most of the time. You can use Interpolation[data, InterpolationOrder -> 0] to get the interpolation behavior you're looking for. $\endgroup$ Commented Nov 12, 2019 at 13:39
  • $\begingroup$ Wow! It looks so obvious now. Still, it would be good to see the implementation. Thanks! $\endgroup$ Commented Nov 12, 2019 at 14:06

2 Answers 2

2
$\begingroup$

You could use Interpolation as suggested @Sjoerd, for example:

data = List @@@ dateToPopulationRules /. d_DateObject :> AbsoluteTime[d];

p = Interpolation[
    data . {{-1, 0}, {0, 1}},
    InterpolationOrder->0
] @* Minus;

where I need to use a double negation to get the end point behavior correct. Visualization:

Plot[
    p[x],
    {x, AbsoluteTime@DateObject[{2019,1,1}], AbsoluteTime@DateObject[{2019,6,1}]},
    PlotRange->{0,10.5},
    Ticks->{
        Charting`DateTicksFunction[Automatic, DateTicksFormat -> {Automatic}],
        Automatic
    },
    Epilog -> {PointSize[Large], Point[data]} 
]

enter image description here

If you want a faster function, you could try using StepFunction from How can the behavior of InerpolationOrder->0 be controlled:

q = StepFunction[data, Right];
Plot[
    q[x],
    {x, AbsoluteTime@DateObject[{2019,1,1}], AbsoluteTime@DateObject[{2019,6,1}]},
    PlotRange->{0,10.5},
    Ticks->{
        Charting`DateTicksFunction[Automatic, DateTicksFormat -> {Automatic}],
        Automatic
    },
    Epilog -> {PointSize[Large], Point[data]} 
]

enter image description here

Speed comparison:

times = RandomReal[
    AbsoluteTime /@ {DateObject[{2019, 1, 1}], DateObject[{2019, 4, 1}]},
    10^5
];

r1 = p[times]; //RepeatedTiming
r2 = q[times]; //RepeatedTiming

r1===r2

{0.14, Null}

{0.0072, Null}

True

So, using StepFunction is almost 20 times faster.

$\endgroup$
2
  • $\begingroup$ Excellent! I assume your StepFunction will eventually be added to the Wolfram Language. $\endgroup$ Commented Nov 13, 2019 at 19:55
  • $\begingroup$ @ArielSepulveda I'm planning to add it as a ResourceFunction, no idea whether somebody might add a kernel function to do this. $\endgroup$
    – Carl Woll
    Commented Nov 13, 2019 at 21:33
2
$\begingroup$

You may useTimeSeries with the ResamplingMethod option.

tsPop = TimeSeries[List @@@ dateToPopulationRules,
  ResamplingMethod -> {"Interpolation", InterpolationOrder -> 0}]

Mathematica graphics

InterpolationOrder -> 0 repeats prior value until new value is defined.

tsPop[DateObject /@ {{2019, 1, 30}, {2019, 2, 1}}]
{5, 10}

Plot with DateListStepPlot.

DateListStepPlot[tsPop, Mesh -> Full]

Mathematica graphics

Hope this helps.

$\endgroup$
1
  • $\begingroup$ Very useful and a new window opened into TimeSeries. Thanks! Based on a simple test using RepeatedTiming q function based on StepFunction seems to be near one order faster than tsPop. $\endgroup$ Commented Nov 13, 2019 at 19:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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