I need help turning a Decay.txt file into an array, the first 1-3 and 5th columns are numbers, the third column is "time elapsed" in integers, but the 4th column is a unit of time (milliseconds, Months, Days) but its spelled out with characters. i cant get this mixed array (numbers and characters) to transfer over to matlab
ideally id like to take the unit of time (4th column) change it to a seconds value, (i.e. hour becomes 3600 seconds) then multiply it by the number in the third column and have a final 4 column array where the 3rd column is simply the time elapsed in seconds
anyone know how to do either of these things?
ive tried
Decay = fopen('Decay.txt','r');
B = fscanf(Decay,'%f',[5 inf]);
which stops and has an error as soon as it hits the 4th column
and
Decay = fopen('Decay.txt','r');
B = fscanf(Decay,'%s',[5 inf]);
but this just creates a 5x10000 column where every single number, decimal, and letter is on its own in its own cell of the array
Your first example
Decay = fopen('Decay.txt','r');
B = fscanf(Decay,'%f',[5 inf]);
Breaks because it can't scan the fourth column (a string) as a number (%f). Your second example doesn't have numbers because you're scanning everything as a string (%s).
The correct specifier for your format should be
'%f %f %f %s %f'
However, if you call fscanfwith it, as per documentation:
If formatSpec contains a combination of numeric and character specifiers, then A is numeric, of class double, and fscanf converts each text characters to its numeric equivalent. This occurs even when formatSpec explicitly skips all numeric fields (for example, formatSpec is '%*d %s').
So this input file:
50 1.2 99 s 0
6.42 1.2 3.11 min 1
22 37 0.01 h 2
Has this (undesired) output:
>> fscanf(Decay, "%f %f %f %s %f", [5, inf])
ans =
50.0000 6.4200 110.0000 104.0000
1.2000 1.2000 1.0000 2.0000
99.0000 3.1100 22.0000 0
115.0000 109.0000 37.0000 0
0 105.0000 0.0100 0
That happens because a matrix in MATLAB can't have multiple data of different types. So, your best bet is scanning into a cell array, which can have any type inside.
B = textscan(Decay, "%f %f %f %s %f")
Returns a cell array with the appropriate types. You can use this output to convert the time data into the same unit and build your vectors/matrix. Columns 1, 2, 3 and 5 are trivial to do, just by accessing the cell B{n} for each n.
Column 4 is a cell array of cells. In each internal cell, there's the string you have. You need to apply a conversion from string to the number you need. For my example, such function would look like:
function scale = DecayScale(unit)
switch(unit)
case 's'
scale = 1;
case 'min'
scale = 60;
case 'h'
scale = 3600;
otherwise
throw('Number format not recognized');
end
end
Which you could then apply to the 4th column like:
timeScale = cellfun(#DecayScale, B{4})
And get the final time as:
timeColumn = B{3} .* timeScale
I'm currently working on a small project on handling time difference on MATLAB. I have two input files; Time_in and Time_out. The two files contain arrays of time in the format e.g 2315 (GMT - Hours and Minute)
I've read both Time_in' and 'Time_out on MATLAB but I don't know how to perform the subtraction. Also, I want the corresponding answers to be in minutes domain only e.g (2hrs 30mins = 150minutes)
this is one of several possible solutions:
First, you should convert your time strings to a MATLAB serial date number. If you've done this, you can do your calculation as you want:
% input time as string
time_in = '2115';
time_out = '2345';
% read the input time as datenum
dTime_in = datenum(time_in,'HHMM');
dTime_out = datenum(time_out,'HHMM');
% subtract to get the time difference
timeDiff = abs(dTime_out - dTime_in);
% Get the minutes of the time difference
timeout = timeDiff * 24 * 60;
Furthermore, to calculate the time differences correctly you also should put some information about the date in your time vector, in order to calculate the correct time around midnight.
If you need further information about the function datenum you should read the following part of the MATLAB documentation:
https://de.mathworks.com/help/matlab/ref/datenum.html
Any questions?
In a recent version of MATLAB, you could use textscan together with datetime and duration data types to do this.
% read the first file
fh1 = fopen('Time_in');
d1 = textscan(fh1, '%{HHmm}D');
fclose(fh1);
fh2 = fopen('Time_out');
d2 = textscan(fh2, '%{HHmm}D');
fclose(fh2);
Note the format specifier '%{HHmm}D' tells MATLAB to read the 4-digit string into a datetime array.
d1 and d2 are now cell arrays where the only element is a datetime vector. You can subtract these, and then use the minutes function to find the number of minutes.
result = minutes(d2{1} - d1{1})
here is my problem.
I have a sorted array of dates that is stored in a circular buffer. I have a pointer to last date in buffer. There is a possibility that some dates are missing. Client requires a range of dates. If low limit date is missing, program should return first closest date that is higher then required one and vice versa for upper limit date.
Here is an example:
Dates in circular buffer (int[18]):
1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28
and if client wants from 8 to 23,
program should return 11,12,13,14,15,21,22,23.
I tried like this :
Notes:
- number between two stars is current date, and diff is number of steps to go to find 8.
- pointer can not be less then 0 or higher then 17.
{1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,*28*}, diff = -20
{*1*,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +7
{1,2,3,4,5,11,12,*13*,14,15,21,22,23,24,25,26,27,28}, diff = -5
{1,2,*3*,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +5 -> (5/2)+1=+3<br />
(if I detect that I will just go x steps forward and x steps backward I split x in half)
{1,2,3,4,5,*11*,12,13,14,15,21,22,23,24,25,26,27,28}, diff = -3 -> (-3/2)-1 = -2
{1,2,3,*4*,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = 4
{1,2,3,4,5,11,12,*13*,14,15,21,22,23,24,25,26,27,28}, diff = -5
{1,2,*3*,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +5 -> (5/2)+1=+3
If we continue like this we will get 13,3,11,4 over and over again.
Notes:
- It is only coincidence that we get 11 here. When I use some real examples, with more dates,this algorithm jumps over some other 4 (or 3) numbers.
- Dates are stored in EEPROM of uC, so reading dates take a while, and I need to find date as quick as it possible (with minimum reads).
Please help.
Set p1 to be the start of the buffer, p2 to be the end. X is what you're looking for.
If the date of p1Date is after X, return p1. If p2Date is before X return p2.
Look at the midpoint between p1 and p2, m. If mDate is after X then p1=m else p2=m.
Repeat until p1=p2.
I was trying to round some fields. When I have 59 days, I want to change it to 60.
The problem is that when I use this code, the 59 is changed to 30 because the round it is 1.
select round(1.9666,0)*30, round(59/30,0)*3'
The result of that query is 60 for the first field and 30 for the second one. The problem is that when I've tried:
select 59/30
The result is 1 and I need the entire answer that is 1.9666...
How can I make it?
Because the number you are dividing by is an INT (the data type of the left side is irrelevant), SQL Server will return an INT as the answer.
If you want a number with a decimal place as your result, you'll need to divide by one.
Don't cast to a FLOAT as the answer is probably not what you want (floats are generally not accurate and are 'approximations'):
SELECT 59 / CAST(30 AS FLOAT) -- = 1.96666666666667
CAST the right-hand side of the division to a DECIMAL:
SELECT 59 / CAST(30 AS DECIMAL(10, 2)) -- = 1.96666
SELECT cast(59 AS FLOAT) / cast(30 AS FLOAT)
Because the original figures are whole numbers, SQL presumes you want a whole number output.
To ensure you get one with the decimal places, you need to first change the data type from an integer int to a floating point float.
This is what the CAST command does.
EDIT: Commenter suggests you cast to DECIMAL instead. The principle is the same, but you need to supply more arguments. To cast to a decimal use something like:
cast(59 as DECIMAL(18, 3))
The first argument (the 18) is the total number of figures you want to permit in the decimal. The second argument (the 3) is the number you want after the decimal point.
The suggestion that it's more accurate is correct - as you'll see if you run the SELECT statements in this answer one after the other. But in this particular case, it only makes a tiny difference.
I have a list of values such as "12000","12345","123456" that need to be converted to currency ("120.00", "123.45", "1234.56"). The only way I know is to convert the value to a string, copy the first strlen()-2 characters to one string (dollars) and the remainging two digits to another string(cents) and then write them as the following:
printf("%s.%s", dollars, cents);
printf("$%.2f", value/100);
Don't use floats for storing or representing monetary amounts. Use longs (if you need more than 4 billion cent use llongs). Its usually a good idea to represent currency in its minimum usable unit, example use 10000 to represent 100Euro). Then the correct way to format these values (assuming 100 cent to the euro or dollar) is:
printf( "%d.%02d", value/100, value%100);
Hope that makes sense...
Calculations with currency values is a complex subject but you cant go far wrong is you always aim to have a rounded answer to the nearest currency unit (cent for example) and always make sure that rounding errors are calculated for (example, to divide 1 dollar three ways you should end up with 33+33+34 or 33+33+33+1).
to prefix values less than $1.00 with 0, use:
printf( "$%0.2f", value / 100.0 );
This will result in $0.25 if value = 25