Basic distance function
I believe we can use a binary search as I did for:
How can the behavior of InterpolationOrder->0 be controlled?
We start by extracting the zeros from your data:
zeros = Cases[Sort @ data, {x_, 0} :> x]
{1, 4, 6}
If data
is always sorted you can omit Sort
.
If your zeros may not always have head Integer
use the rule {x_, n_ /; n==0} :> x
I'll use the Combinatorica`BinarySearch
function and Floor
for simplicity; use a compiled version of Leonid's bsearchMin if you need greater speed.
Now:
Needs["Combinatorica`"]
distanceFromLast[lst_List][val_?NumericQ] :=
val - lst[[ Floor @ BinarySearch[lst, val] ]]
Plot[distanceFromLast[zeros][x], {x, 0, 10}, AspectRatio -> Automatic]
Or equivalently:
distanceFromLast[zeros] /@ Range[0, 10, 0.1] // ListLinePlot
Extension and generalization
The output above doesn't quite match what question requested: the function should start climbing from zero where it does in ListPlot[data, InterpolationOrder -> 0]
rather than immediately after a zero. If the data is regular as shown (sequential, evenly spaced indexes) this is simply a matter of an offset and threshold which may be done with Max[0, value - 1]
:
Plot[Max[0, #-1]& @ distanceFromLast[zeros][x], {x, 0, 10}, AspectRatio -> Automatic]
Generalizing this to arbitrary $(x, y)$ data is more involved because the gaps vary in size. I chose to use SplitBy
as a foundation. I will show the use of several functions and include the code for them below.
First I generate some data:
n = 40;
SeedRandom[1]
data = {Sort @ RandomReal[14, n], RandomInteger[4, n]}\[Transpose];
It looks like this:
p0 = ListLinePlot[data, InterpolationOrder -> 0, AspectRatio -> Automatic]
Then I create a distance function using my makeDistFun
, plot it, and Show
the two:
f1 = makeDistFun[data];
p1 = Plot[f1[x], {x, 0, 14},
PlotStyle -> Directive[Thick, Red, Dashed],
PlotRange -> All];
Show[p0, p1, Frame -> True]
If you merely wish to draw the line it will be more efficient to use the draw
function I provide:
p2 = draw[data, Green];
Show[p0, p2, Axes -> False]
Code
Note: I again use the Combinatorica BinarySearch
function for brevity, and again bsearchMin
or similar will be faster if it matters in practice.
breaks[data : {{_, _} ..}] :=
Module[{d2, split},
d2 = Append[data, Last[data] {1, 0}];
split = SplitBy[d2, Unitize @ #[[2]] &][[All, 1, 1]];
Partition[If[d2[[1, 2]] == 0, Rest@#, #] &[split], 2]
]
Needs["Combinatorica`"]
makeDistFun[data : {{_, _} ..}] :=
Module[{starts, ends},
{starts, ends} = breaks[data]\[Transpose];
With[{n = Floor @ BinarySearch[starts, #]},
If[# < ends[[n]], # - starts[[n]], 0]
] &
]
draw[data : {{_, _} ..}, {dir__} | dir__, opts : OptionsPattern[]] :=
Graphics[{dir,
Line @ Flatten[{{#, 0}, {#2, #2 - #}, {#2, 0}} & @@@ breaks[data], 1]
}, opts
]
InterpolationOrder->0
the other one does not have.Length@data
gives 6 andLength@result
gives 7. I don't really get what you want.., am I the only one? $\endgroup$InterpolationOrder ->0
as the data the graph represents is assumed to be constant over the day. For the results, I want to know the time that has passed since the last time data=0. So in this case I want a lineair function.Length@data
andLength@results
are not equal because I needed to specify two points at time 4 to let the graph drop to 0 directly (as from time 4 on, it has been 0 days again since the last 0) $\endgroup${2,0}
and{4,0}
in theresult
set of data? *Still confused* $\endgroup$data
. But then given in a list, showing (per day) the time since the last 0. - Maybe theresult
I put in is a bit confusing. Result is not what I want, its what I have constructed myself to show the plot I want as a result. $\endgroup$Position[data, {_, 0}]
and thenDifferences@Flatten@%-1
? $\endgroup$