# data manipulation in log-log scale

Say that I have the following:

y[t_] := 2 + (2/10)*(1 - Exp[-t/11])

timedat = N[Table[{t, y[t]}, {t, 0, 10, 1/1000}]];


How is it possible to construct an algorithm that it will pick data uniformly spaced in log time from a set of data uniformly spaced in time, as the above timedat?

Basically, what I want is the Mathematica counterpart of the following Matlab algorithm

% log sampling, user picks the initial time and increment time
% xi(:,1) time (equally spaced in time)
% xi(:,2) compliance
ndp = length(xi(:,1)); %# data points read
tf = xi(ndp,1); %final time
logti = -1; %log of initial time to sample, user choice
del_logti = 0.1; %log time interval to sample, user choice
logt = [logti:del_logti:log10(tf)]; %equally spaced in log scale
tr = 10.^logt; %back to time scale
nr = length(tr); %number of newly sampled data
if tr(nr)~=tf;
tr = [tr,tf]; %add the final time
nr = length(tr); %number of newly sampled data
end
rcount = 1;
for i=1:ndp
if xi(i,1) >= tr(rcount) %say 10^-0.1
xo(rcount,:) = xi(i,:); %copy data equally spaced in log(time)
rcount = rcount+1;
end
if rcount > nr, break, end;
end


which I found here ; page 4.

Thanks.

• – C. E. Sep 14 '15 at 9:49
• @Pickett: Thanks a lot for the link. – Dimitris Sep 14 '15 at 10:14

Using Range, Length, Nearest, Flatten, Select and MemberQ you can extract uniformly spaced log time from your uniformly spaced in time data.

First an example is shown using 0.1 for the minimum time, 10.0 for the maximum time and 21 for the number of samples.

Then it will be wrapped up into a function.

Data is generated as in your question.

y[t_] := 2 + (2/10)*(1 - Exp[-t/11])
timedat = N[Table[{t, y[t]}, {t, 0, 10, 1/1000}]];


Evenly spaced logarithmic time is created from 0.1 to 10 in 21 steps.

equalLogTime = Range[Log[0.1], Log[10], (Log[10] - Log[0.1])/(21 - 1)]


{-2.30259, -2.07233, -1.84207, -1.61181, -1.38155, -1.15129, -0.921034, -0.690776, -0.460517, -0.230259, 4.44089*10^-16, 0.230259, 0.460517, 0.690776, 0.921034, 1.15129, 1.38155, 1.61181, 1.84207, 2.07233, 2.30259}

The evenly spaced logarithmic time is converted back to linear

equalTime = Exp[equalLogTime]


{0.1, 0.125893, 0.158489, 0.199526, 0.251189, 0.316228, 0.398107, 0.501187, 0.630957, 0.794328, 1., 1.25893, 1.58489, 1.99526, 2.51189, 3.16228, 3.98107, 5.01187, 6.30957, 7.94328, 10.}

The time from the timedat that are closest to these numbers are found.

timedatEqualTime = Flatten[Nearest[timedat[[All, 1]], #] & /@ equalTime]


{0.1, 0.126, 0.158, 0.2, 0.251, 0.316, 0.398, 0.501, 0.631, 0.794, 1., 1.259, 1.585, 1.995, 2.512, 3.162, 3.981, 5.012, 6.31, 7.943, 10.}

timedatEqualTime is used to extract the {time, value} pairs from timedat

dataEvenLogTime = Select[timedat, MemberQ[timedatEqualTime, #[[1]]] &]


Now, putting it into a function:

extractEqualLogTime[data_, timeMin_, timeMax_, numberPoints_] :=
Module[
{
equalLogTime,
equalTime,
dataEqualTime
},

equalLogTime = Range[Log[timeMin], Log[timeMax],
(Log[timeMax] - Log[timeMin])/(numberPoints - 1)];

equalTime = Exp[equalLogTime];

dataEqualTime = Flatten[Nearest[data[[All, 1]], #] & /@ equalTime];

Select[data, MemberQ[dataEqualTime, #[[1]]] &]
]


Applying the function

dataEvenLogTime = extractEqualLogTime[timedat, 0.1, 10, 21]


Now it is plotted on a logarithmic time scale to validate the even spacing.

ListLogLinearPlot[dataEvenLogTime]


• Thanks for your answer! Where timedat is defined? – Dimitris Sep 15 '15 at 8:20
• When I apply your function I get an error message Nearest::dmtch: The dimension of ...does not match. I guess sth I'm doing wrong. – Dimitris Sep 15 '15 at 8:34
• timedat comes from your original question. y[t_] := 2 + (2/10)*(1 - Exp[-t/11]) and then timedat = N[Table[{t, y[t]}, {t, 0, 10, 1/1000}]]. After executing those two statements and then copying the definition for extractEqualLogTime from the answer and executing ListLogLinearPlot[dataEvenLogTime] it should work. I'll add it to the answer. – Jack LaVigne Sep 15 '15 at 10:37
• Sorry for the misunderstanding! Thank you one more time for your nice workaround. – Dimitris Sep 15 '15 at 10:41
• For the sake of completeness. For versions prior to 10.1 one could try sth like Flatten[Nearest[timedat[[All, 1]], equalTime[[#]]] & /@ Range[Length[equalTime]]] . The output is again the same; i.e. {0.1, 0.126, 0.158, 0.2, 0.251, 0.316, 0.398, 0.501, 0.631, 0.794, \ 1., 1.259, 1.585, 1.995, 2.512, 3.162, 3.981, 5.012, 6.31, 7.943, 10.} . Flatten@Nearest[timedat[[All, 1]], equalTime] produces the error message Nearest::dmtch: The dimension of ... does not match. Unfortunately, Nearest[data,{x1,x2,…}] was introduced in version 10.1. – Dimitris Sep 17 '15 at 16:41

The algorithm does two things:

1. Sample uniformly in log space and then convert it back to time space.
2. Select data points in time space corresponding to the samples generated in (1).

logspace from [13226] can be used for (1):

logspace[increments_, start_?Positive, end_?Positive] :=
Exp@Range[Log@start, Log@end, Log[end/start]/increments]


For step 2, Nearest is useful:

logSpaceSample[samplepoints_, datapoints_] :=
First@*Nearest[First[#] -> # & /@ datapoints] /@ samplepoints


Example usage:

y[t_] := 2 + (2/10)*(1 - Exp[-t/11])

timedat = N[Table[{t, y[t]}, {t, 0, 10, 1/1000}]];

res = logSpaceSample[
logspace[10, 1, 10],
timedat
];

ex = {{First[#], 0} & /@ res, {Log@First[#], 1} & /@ res};
Graphics[{
ColorData[97][1], Point@ex[[1]],
ColorData[97][2], Point@ex[[2]]
}]


You may note that logspace does not take the same arguments for sampling that your algorithm does. It takes increments number of samples from start to end. You can rewrite it as you see fit.

• I have Mathematica 10.0 and I do not appear to have Subdivide. Was it added later? – ArgentoSapiens Sep 14 '15 at 20:30
• @ArgentoSapiens You are right, it was added in 10.1. I updated the answer to reflect this. – C. E. Sep 14 '15 at 20:44
• @Pickett I like your answer but something is wrong with logspace. I compare the answers from Exp@Range[Log[0.1], Log[10], (Log[10] - Log[0.1])/(21 - 1)] with the output from logspace[20, 0.1, 10] and they are different. With the Range approach I get {0.1, 0.125893, 0.158489, 0.199526, 0.251189, 0.316228, 0.398107, \ 0.501187, 0.630957, 0.794328, 1., 1.25893, 1.58489, 1.99526, 2.51189, \ 3.16228, 3.98107, 5.01187, 6.30957, 7.94328, 10.} and using logspace I get – Jack LaVigne Sep 14 '15 at 21:14
• {0.1, 0.226864, 0.369821, 0.530915, 0.712446, 0.917007, 1.14752, \ 1.40727, 1.69998, 2.02983, 2.40151, 2.82036, 3.29233, 3.82419, \ 4.42352, 5.09888, 5.85991, 6.7175, 7.68388, 8.77287, 10.}. If I call the results time1 and time2 the ratio of time1[[2;;-1]]/time1[[1;;-2]] is a constant (as it should be). The ratio of time2[[2;;-1]]/time2[[1;;-2]] is variable. Please take another look. – Jack LaVigne Sep 14 '15 at 21:16
• @Pickett I looked at the logspace link supplied and have concluded that it only works when the starting value is 1.0. That may be what the designer had in mind, – Jack LaVigne Sep 14 '15 at 21:44

Ok here is the workaround. Actually it's the transfer of above MatLab code in Mathematica's procedural programming. Thanks to Sector, because his comments made me work harder with it!!

I would appreciate now other ways of arriving in the same result which makes use of Mathematica functional or/and rule-based programming (and of course make the program better:-)!).

Here we go...

fun[\[Lambda]_] :=
1 + 9*(1 -
E^(-\[Lambda]/
10))(*the function to generate the date; \[Lambda] denotes \
times*)

xi = Transpose[
N[Table[{\[Lambda], y[\[Lambda], \[Tau]o]}, {\[Lambda], 1, 200,
0.01}]]];(*data; xi[[1]]= time; xi[[2]]=copliance*)
ndp = Length[xi[[1]]];(*number of time data points*)
tf = Last[xi[[1]]];(*final time*)
logti = -1;(*log of initial time to sample; user's choice*)
dellogti = 0.001;(*log time interval to sample; user's choice*)
logt = Range[logti, Log[10, tf],
N@dellogti];(*equally spaced in log scale*)
tr = logt /. a_ -> 10^a;(*back to time scale*)
nr = Length[tr];(*number of newly sampled data*)
If[tr[[nr]] != tf, tr = Append[tr, tf](*add the final time*);
nr = Length[tr](*number of newly sampled data*);]
rcount = 1;
xo = {};
For[i = 1, i <= ndp, i++,
If[xi[[1, i]] >= tr[[rcount]],
xo = Append[
xo, {xi[[1, i]],
xi[[2, i]]}](*copy data equally spaced in log time*);
rcount = rcount + 1]; If[rcount > nr, Break[]]]
Short[xi]
Short[xo = Transpose[xo]]
{Max[Differences[Log[10, #] & /@ xo[[1]]]],
Min[Differences[Log[10, #] & /@ xo[[1]]]]}


with outputs

{{1., 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08,<<19884>>,199.93, 199.94, 199.95000000000002, 199.96, 199.97, \
199.98000000000002, 199.99, 200.},<<1>>}

{{1., 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09,<<3284>>,196.79, 197.25, 197.70000000000002, 198.16, 198.61, 199.07, \
199.53, 199.99, 200.},<<1>>}

{0.00432137, 0.0000217153}