Skip to content

Commit 780ecbb

Browse files
committed
add find, findIndex, "in" for Series
1 parent bb9fe40 commit 780ecbb

File tree

1 file changed

+113
-25
lines changed

1 file changed

+113
-25
lines changed

source/mir/timeseries.d

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,50 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
243243
assert(series.data == [10, 12, 10, 13]);
244244
}
245245

246+
/++
247+
This function uses a search with policy sp to find the largest left subrange on which
248+
`t < moment` is true for all `t`.
249+
The search schedule and its complexity are documented in `std.range.SearchPolicy`.
250+
+/
251+
auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, Time)(Time moment)
252+
{
253+
return this[0 .. time.assumeSorted.lowerBound!sp(moment).length];
254+
}
255+
256+
/++
257+
This function uses a search with policy sp to find the largest left subrange on which
258+
`t > moment` is true for all `t`.
259+
The search schedule and its complexity are documented in `std.range.SearchPolicy`.
260+
+/
261+
auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, Time)(Time moment)
262+
{
263+
return this[$ - time.assumeSorted.upperBound!sp(moment).length .. $];
264+
}
265+
266+
///
267+
bool contains(Time)(Time moment)
268+
{
269+
size_t idx = time.assumeSorted.lowerBound(moment).length;
270+
return idx < _data._lengths[0] && time[idx] == moment;
271+
}
272+
273+
///
274+
auto opBinaryRight(string op : "in", Time)(Time moment)
275+
{
276+
size_t idx = time.assumeSorted.lowerBound(moment).length;
277+
bool cond = idx < _data._lengths[0] && time[idx] == moment;
278+
static if (__traits(compiles, &_data[size_t.init]))
279+
{
280+
if (cond)
281+
return &_data[idx];
282+
return null;
283+
}
284+
else
285+
{
286+
return bool(cond);
287+
}
288+
}
289+
246290
/// ndslice-like primitives
247291
bool empty(size_t dimension = 0)() const @property
248292
if (dimension < packs[0])
@@ -378,36 +422,11 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
378422
}
379423
}
380424

381-
/++
382-
This function uses a search with policy sp to find the largest left subrange on which
383-
`t < moment` is true for all `t`.
384-
The search schedule and its complexity are documented in `std.range.SearchPolicy`.
385-
+/
386-
auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, Time)(Time moment)
387-
{
388-
return this[0 .. time.assumeSorted.lowerBound!sp(moment).length];
389-
}
390-
391-
/++
392-
This function uses a search with policy sp to find the largest left subrange on which
393-
`t > moment` is true for all `t`.
394-
The search schedule and its complexity are documented in `std.range.SearchPolicy`.
395-
+/
396-
auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, Time)(Time moment)
397-
{
398-
return this[$ - time.assumeSorted.upperBound!sp(moment).length .. $];
399-
}
400-
401425
/// ditto
402426
auto save()() @property
403427
{
404428
return this;
405429
}
406-
407-
bool contains(Time)(Time momemt)
408-
{
409-
return time.assumeSorted.contains(momemt);
410-
}
411430
}
412431

413432
/// 1-dimensional data
@@ -418,7 +437,10 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
418437
auto series = time.series(data);
419438

420439
assert(series.contains(2));
440+
assert( ()@trusted{ return (2 in series) is &data[1]; }() );
441+
421442
assert(!series.contains(5));
443+
assert( ()@trusted{ return (5 in series) is null; }() );
422444

423445
assert(series.lowerBound(2) == series[0 .. 1]);
424446
assert(series.upperBound(2) == series[2 .. $]);
@@ -503,6 +525,72 @@ enum isSeries(U : Series!(TimeIterator, kind, packs, Iterator), TimeIterator, Sl
503525
/// ditto
504526
enum isSeries(U) = (size_t[]).init;
505527

528+
529+
/++
530+
Finds an index such that `series.time[index] == moment`.
531+
532+
Params:
533+
series = timeseries
534+
moment = time moment to find in the series
535+
Returns:
536+
`size_t.max` if the series does not contain the moment and appropriate index otherwise.
537+
+/
538+
size_t findIndex(TimeIterator, SliceKind kind, size_t[] packs, Iterator, Time)(Series!(TimeIterator, kind, packs, Iterator) series, Time moment)
539+
{
540+
import std.range: assumeSorted;
541+
542+
auto idx = series.time.assumeSorted.lowerBound(moment).length;
543+
if (idx < series._data._lengths[0] && series.time[idx] == moment)
544+
{
545+
return idx;
546+
}
547+
return size_t.max;
548+
}
549+
550+
///
551+
@safe pure nothrow version(mir_test) unittest
552+
{
553+
auto time = [1, 2, 3, 4].sliced;
554+
auto data = [2.1, 3.4, 5.6, 7.8].sliced;
555+
auto series = time.series(data);
556+
557+
assert(series.data[series.findIndex(3)] == 5.6);
558+
assert(series.findIndex(0) == size_t.max);
559+
}
560+
561+
/++
562+
Finds a backward index such that `series.time[$ - backward_index] == moment`.
563+
564+
Params:
565+
series = timeseries
566+
moment = time moment to find in the series
567+
Returns:
568+
`0` if the series does not contain the moment and appropriate backward index otherwise.
569+
+/
570+
size_t find(TimeIterator, SliceKind kind, size_t[] packs, Iterator, Time)(Series!(TimeIterator, kind, packs, Iterator) series, Time moment)
571+
{
572+
import std.range: assumeSorted;
573+
574+
auto idx = series.time.assumeSorted.lowerBound(moment).length;
575+
auto bidx = series._data._lengths[0] - idx;
576+
if (bidx && series.time[idx] == moment)
577+
{
578+
return bidx;
579+
}
580+
return 0;
581+
}
582+
583+
///
584+
@safe pure nothrow version(mir_test) unittest
585+
{
586+
auto time = [1, 2, 3, 4].sliced;
587+
auto data = [2.1, 3.4, 5.6, 7.8].sliced;
588+
auto series = time.series(data);
589+
590+
assert(series.data[$ - series.find(3)] == 5.6);
591+
assert(series.find(0) == 0);
592+
}
593+
506594
/++
507595
Sorts time-series according to the `less` predicate applied to time observations.
508596

0 commit comments

Comments
 (0)