How can I stop Matlab rounding decimals when subtracting Datetime variables - matlab

I am having some issues in Matlab to do with rounding errors with datetime typed variables.
I have an array, lets call it 't', and it is of type datetime.
Say for example, t(2) = 00:01:35.6889999, and t(1) = 00:01:35.3549042.
If I try to do t(2)-t(1), all I get is an answer of type 'duration' of 00:00:00 .
I would like to find the difference between these times and keep the precision!
Any help, or directions to links that directly cater/relate to an issue like this would be appreciated! I'm not that familiar with using datetime & duration typed variables in Matlab!
Extra info: I am using Matlab R2017a
Edit: I have Format Long; written in my script.

Precision is not being lost, you just need to change the display format.
The default display format is HH:MM:SS:
>> A = duration(0, 0, 0, 1.25) % 1.25 MS
A =
duration
00:00:00
You can modify the format to display fractional parts. For example:
>> A.Format = 's' % Seconds only
A =
duration
0.00125 sec
>> A.Format = 'hh:mm:ss.SSSSSSSS' % HMS, up to 9 fractional second digits
A =
duration
00:00:00.00125000
You can also use helper functions like milliseconds or seconds to return double arrays:
>> seconds(A)
ans =
0.0013
>> milliseconds(A)
ans =
1.2500

This should work if only seconds vary in the two dates
second(t(1))-second(t(2))

The duration object actually has the proper precision. It just doesn't display it unless you set the format.
>> dur = duration(t(2) - t(1), 'Format', 's')
dur =
duration
0.3341 sec
Whether you set the format or not, you can grab the seconds directly from the duration object.
>> format long
>> seconds(dur)
ans =
0.334095700000000
>> seconds(t(2) - t(1))
ans =
0.334095700000000

Related

How do I format X Axis tick labels when using Epoch seconds as X axis values?

I have the following data set:
octave:42> yy
yy =
1420174800.00000 21.70000 21.93000 21.64000 21.81000 1949882.00000
1420434000.00000 21.92000 22.23000 21.90900 22.17000 3189214.00000
1420520400.00000 22.15000 22.51000 22.08000 22.41000 4810496.00000
1420606800.00000 22.21000 22.27500 22.07000 22.11000 2397372.00000
1420693200.00000 21.93000 21.93600 21.70000 21.73000 2797149.00000
1420779600.00000 21.68000 21.98000 21.68000 21.90000 2377333.00000
1421038800.00000 21.88000 22.15000 21.87000 22.07000 2347871.00000
1421125200.00000 21.94000 22.30000 21.76500 22.15000 5255049.00000
1421211600.00000 22.40000 22.51900 22.24000 22.25000 5843980.00000
1421298000.00000 22.18000 22.48000 22.14000 22.47990 3498303.00000
If I plot it I get the following chart:
octave:44> plot(yy(:,1), yy(:,5))
Following the Octave documentation (https://octave.sourceforge.io/octave/function/datetick.html) I have tried the following to get the Years displayed instead of the UNIX Epoch seconds values.
octave:48> plot(yy(:,1), yy(:,5))
octave:49> datetick('x','YYYY')
But this is the result:
I would like to learn how to display the YEAR (YYYY) and the year and month (YY-MM). I am not sure if a Matlab solution could also fit in Octave.
Short version
plot( arrayfun( #(x) datenum([1970, 1, 1, 0, 0, x]), yy(:,1) ), yy(:,5) )
datetick( 'x', 'yyyy' )
Explanation
The numbers you have are unix epochs (i.e. seconds since 1970-1-1 00:00:00 UTC).
The reason you're getting weird numbers out of datetick is because it does not expect unix epochs as input, but "date numbers" (fractional number of days since 0000-1-0 00:00:00 UTC, whatever that means).
What you can do, is use your values to convert to datenums, via a datevec. E.g. your first value, i.e. 1420174800, can be converted to a datenum like so
datenum([1970,1,1,0,0,1420174800]) % i.e. the 1420174800th second of 1970-01-01
% ans = 7.3597e+05
datestr( datenum([1970,1,1,0,0,1420174800]), 0 )
% ans = 02-Jan-2015 05:00:00
Therefore, you could convert all your unix epochs to datenums, e.g. like so
DateNums = arrayfun( #(x) datenum([1970, 1, 1, 0, 0, x]), yy(:,1) )
And then plot using the DateNums instead, and use datetick as expected.
The above solution would work identically in octave and matlab.

Matlab reading numbers with higher precision

I have made this scripts that calculates the frequency of a given dataset, but matlab is not precise enough, is it possible to make matlab read in more accurat numbers and not cut off the numbers? I want it to use 8 digits (0.12345678) instead of 4 (0.1234) that is does now
fid = fopen('forceCoeffs.dat','rt');
A = textscan(fid, '%f%f%f%f%f%f', 'HeaderLines',9,'Collect', 9);
A = A{1};
fclose(fid);
t = A(:,1);
Fs = 1/(A(1,1));
x = A(:,2)
x = detrend(x,0);
xdft = fft(x);
freq = 0:Fs/length(x):Fs/2;
xdft = xdft(1:length(x)/2+1);
plot(freq,abs(xdft));
[~,I] = max(abs(xdft));
fprintf('Maximum occurs at %d Hz.\n',freq(I));
File: https://drive.google.com/file/d/0B9CEsYCSSZUSb1JmcHRkbFdWYUU/view?usp=sharing
Thank you for including the forceCoeffs.dat file as it allowed me to run your code. Here is an explanation of what you are seeing.
First I want to point out that MATLAB is not rounding anything. You can check the data type of A to ensure you have enough precision.
>> class(A)
ans =
double
And since you are reading in the file using %f for each column, MATLAB will use all the bits provided by the double type. Ok, now take a look at the contents of your file. The first column has only 2 decimals of precision at most.
0.05 -7.013874e-09 1.410717e+02 -6.688450e-02 -3.344226e-02 -3.344224e-02
...
349.95 -1.189524e-03 1.381022e+00 -2.523909e-01 -1.273850e-01 -1.250059e-01
350 -1.423947e-03 1.380908e+00 -2.471767e-01 -1.250123e-01 -1.221644e-01
Since no more is needed MATLAB only prints four decimal places when you look at the variable in the variable explorer. Try looking at one of the other columns to see what I am talking about. I commented out the A = A{1} part of your code and looked at the second column. When clicking on the number you see the full precision.
You can use a long type to display 16 digits
To get more than 4 digits precision, you can use
format long
However, to get exactly 8 digits, you need to round it. If your number is a then let use:
format long
round(1e8*a)*1e-8

Generate timestamp series in Matlab?

all
I wonder if there is a way to generate timestamp series in Matlab ?
I assume there will be a start time, a end time, and a frequency.
It is simple to generate normal series using 1:1:100 (1 to 100 by 1)
How I can use a similar way to generate a time stamp series?
For instance, I specify start time as 9am, up to 10am, I want to generate something like 9:00:00:000, 9:00:00:500, 9:00:01:000, ....
gaped by 500 millisecond
Or even better, include date as well.
Use datenum, the only problem you might have is that your colliding with a gap second/day or summer savings time if you're spanning a long time period (but I don't think that's implemented in datestr as you can read here).
Play around with datenum, now and datestr
starttime = datenum(2000, 1, 1, 9, 0, 0);
dt = 0.500/86400; % datenum is a serial time format with 1 = 1 day = 86400 sec
N = 5;
timevec = starttime + dt*(0:(N-1));
>> datestr(timevec, 'HH:MM:SS.FFF')
ans =
09:00:00.000
09:00:00.500
09:00:01.000
09:00:01.500
09:00:02.000
Starting from 2015a, you can use the milliseconds function to build a vector of timesteps between to time points:
start = datetime('2017/1/3 9:00:00:000','InputFormat','yyyy/MM/dd H:mm:ss:SSS');
step = milliseconds(500);
fin = datetime('2017/1/3 10:00:00:000','InputFormat','yyyy/MM/dd H:mm:ss:SSS');
time_vec = start:step:fin;
If you don't define the date explicitly it will choose the current date.
You can also have one structure for both the time and the data, you can use the timeseries class (using start from above):
data = rand(7201,1);
ts = timeseries(data,'Name','MyTs');
ts.TimeInfo.StartDate = start;
ts.TimeInfo.Units = 'milliseconds';
ts = setuniformtime(ts,'Interval',500);
This will create a time series object:
>> ts
timeseries
Common Properties:
Name: 'MyTs'
Time: [7201x1 double]
TimeInfo: [1x1 tsdata.timemetadata]
Data: [7201x1 double]
DataInfo: [1x1 tsdata.datametadata]
with the following time info:
>> ts.TimeInfo
tsdata.timemetadata
Package: tsdata
Uniform Time:
Length 7201
Increment 500 milliseconds
Time Range:
Start 03-Jan-2017 09:00:00
End 03-Jan-2017 10:00:00
Common Properties:
Units: 'milliseconds'
Format: ''
StartDate: '03-Jan-2017 09:00:00'
It depends on your needs, but you can consider using the combination of datetime() and one or many of days(), hours(), minutes(), seconds() etc. functions.
Lets write some code:
start=datetime(1985,07,13,9,0,0); % your start date
steps=seconds(0:0.5:100); % your vector with steps
timeseries=start+steps; % your time series
you can also set format for displaying data that meets your needs, to do so check datetime properties manual.

Matlab fprintf to keep significant figures and rightmost zeroes

Let's say I have a random variable a=1.2400, and I want to print it with four significant figures, i.e., 1.240. How would I go about that?
fprintf('%0.4g',a) % drops rightmost zero
fprintf('%0.3f',a) % give too many sig figs if a >= 10
Using '%g' drops the important zeros, and with '%f' I can only specify the number of digits after the decimal, which results in too many significant figures if, say, a=10.04. I'm not too familiar with formatting ,but there has to be a simple method. I haven't found it in my searches.
If the values to be printed are all less than 10000, you can do the following. (Sorry, only tested in octave.)
octave:62> a = 1.24
a = 1.2400
octave:63> sprintf('%.*f\n', 3-floor(log10(abs(a))), a)
ans = 1.240
octave:64> a = 234.56
a = 234.56
octave:65> sprintf('%.*f\n', 3-floor(log10(abs(a))), a)
ans = 234.6
For more about the expression floor(log10(abs(a))), see How can I get the exponent of each number in a np.array?
If you don't mind exponential notation, another alternative is to use '%.3e' to always get the same number of signficant digits:
octave:70> a = 1.24
a = 1.2400
octave:71> sprintf('%.3e\n', a)
ans = 1.240e+00
octave:72> a = 234.56
a = 234.56
octave:73> sprintf('%.3e\n', a)
ans = 2.346e+02
I decided to build on the answer by Warren, and I wrote a function that should work for both small and large numbers alike. Perhaps someone will improve on this, but I am pleased with it.
function str=sigfigstr(a,sigfigs)
numdecimal = floor(log10(abs(a)));
if sigfigs - numdecimal < 0
str=sprintf('%.0f',round(a,sigfigs,'significant'));
else
str=strip(sprintf('%.*f\n', sigfigs-floor(log10(abs(a))), a));
end
Here are a few examples if it in action in Matlab
>> sigfigstr(.000012431634,3)
ans = '0.0000124'
>> sigfigstr(26666,3)
ans = '26700'

MatLab - 9.45 representation and round

>> a = 12.5 * 9.45
a =
1.181250000000000e+02
>> round(a * 100) /100
ans =
1.181200000000000e+02
The rounded value should be 118.13, not 118.12.
If you type 9.45 in MatLab command line, it can't be represented :
>> 9.45
ans =
9.449999999999999
If I set the numeric format to short, the final result is the same.
>> a = 12.5 * 9.45
a =
118.1250
>> round(a * 100) / 100
ans =
118.1200
Can someone explain that? Any workaround?
You could try something like John D'Errico's hpf class.
This will give the result you are expecting
round(hpf('12.5') * hpf('9.45') * 100)/100
ans =
118.13
F = hpf('9.45')
F =
9.45
Sounds like you've got some settings that are messing with your output precision. It looks like your current settings want 15 decimal places(possibly longG)?, and with MATLABs floating point numbers you won't get 100% precision that far.
You can change the format of your display output, but the precision is going to be an issue to that level regardless.
The relevant documentation.