How to fill up line with delimiters in Matlab - matlab

I have a cell array A, which I wish to print as a table. The first columns and first row are headers. For example I have
A:
1 2 3
4 5 6
7 8 9
And I want the output to look like this:
A:
1 ||2 |3
-------------
4 ||5 |6
7 ||8 |9
The vertical bars are not problematic. I just dont know how to print out the horizontal line. It should be more flexible then just disp('-------'). It should resize depending on how big my strings in my cells are.
So far I only implemented the ugly way which just displays a static string '-----'.
function [] = dispTable(table)
basicStr = '%s\t| ';
fmt = strcat('%s\t||',repmat(basicStr,1,size(table,2)-1),'\n');
lineHeader = '------';
%print first line as header:
fprintf(1,fmt,table{1,:});
disp(lineHeader);
fprintf(1,fmt,table{2:end,:});
end
Any help is appreciated. Thanks!

You are not going to reliably be able to compute the width of a field since you are using tabs whose width can vary from machine to machine. Also if you're trying to display something in a tabular structure, it's best to avoid tabs just in case two values are different by more than 8 characters which would lead to the columns not lining up.
Rather than using tabs, I would use fixed-width fields for your data, then you know exactly how many - characters to use.
% Construct a format string using fixed-width fonts
% NOTE: You could compute the needed width dynamically based on input
format = ['%-4s||', repmat('%-4s|', 1, size(table, 2) - 1)];
% Replace the last | with a newline
format(end) = char(10);
% You can compute how many hypens you need to span your data
h_line = [repmat('-', [1, 5 * size(table, 2)]), 10];
% Now print the result
fprintf(format, table{1,:})
fprintf(h_line)
fprintf(format, table{2:end,:})
% 1 ||2 |3
% ---------------
% 4 ||7 |5
% 8 ||6 |9

Related

Read a complex, and long text file in Matlab

I have a very long text file which contains the data from 4 different stations with different time steps:
1:00
station 1
a number 1 (e.g.0.6E-06)
matrix1 (41x36)
station 2
number 2 (e.g.0.1E-06)
matrix2 (41x36)
station 3
number 3 (e.g.0.2E-06)
matrix3 (41x36)
station 4
number 4 (e.g.0.4E-06)
matrix4 (41x36)
2:00
station 1
a number (e.g.0.24E-06)
matrix5 (41x36)
station 2
a number (e.g.0.3E-06)
matrix6 (41x36)
station 3
number (e.g.0.12E-06)
matrix7 (41x36)
station 4
number (e.g.0.14E-06)
matrix8 (41x36)
.....
and so on
I need to read this data by each station and each step, and noted that each matrix should be scaled by multiplying with a number above it. An example is here: https://files.fm/u/sn447ttc#/view/example.txt
Could you please help?
Thank you a lot.
My idea here would be to read the textfile using fopen and textscan. Afterwards you can search for appearances of the Keyword FACTOR to subdivide the output. Here's the code:
fid=fopen('example.txt'); % open the document
dataRaw=textscan(fid,'%s','Delimiter',''); % read the file with no delimiter to achieve a cell array with 1 cell per line of the text file
fclose(fid); % close the document
rows=cellfun(#(x) strfind(x,'FACTOR'),dataRaw,'uni',0); % search for appearances of 'FACTOR'
hasFactor=find(~cellfun(#isempty,rows{1})); % get rownumbers of the lines that contain the word FACTOR
dataRaw=dataRaw{1}; % convert array for easier indexing
for ii=1:(numel(hasFactor)-1) % loop over appearances of the word FACTOR
array=cellfun(#str2num,dataRaw(hasFactor(ii)+2:hasFactor(ii+1)-1),'uni',0); % extract numerical data
output{ii}=str2num(dataRaw{hasFactor(ii)+1})*cat(1,array{:}); % create output scaled by the factor
end
array=cellfun(#str2num,dataRaw(hasFactor(end)+2:end),'uni',0);
output{end+1}=str2num(dataRaw{hasFactor(end)+1})*cat(1,array{:}); % These last 2 lines add the last array to the ouput
outputMat=cat(3,output{:}); % convert to a 3-dimensional matrix
outputStations=[{output(1:4:end)} {output(2:4:end)} {output(3:4:end)} {output(4:4:end)}]; % Sort the output to have 1 cell for each station
outputColumnSums=cellfun(#(x) cellfun(#sum,x,'uni',0),outputStations,'uni',0); % To sum up all the columns of each matrix
outputRowSums=cellfun(#(x) cellfun(#(y) sum(y,2),x,'uni',0),outputStations,'uni',0);
This approach is pretty slow and probably can be vectorized, but if you don't need it to be fast it should do the job. I created a cell-output with 1 cell per array and a 3 dimensional array as optional output. Hope that's fine with you
I have looked into your situation and it seems that the problem not trivial as anticipated. Keep in mind that if I have made mistakes on the assumption of the location of the data, you can let me know so I can edit it, or you can just change the numbers to that which suits your case. In this case, I initially loaded the delimited file into an Excel spreadsheet, just to visualize it.
After reading up on dlmread, I found that one can specify the exact rows and columns to pull from example.txt, as shown here:
data = dlmread('example.txt', ' ', [4 1 45 37]); % [r1 c1 r2 c2]
data2 = dlmread('example.txt', ' ', [47 1 88 37]);
The result of which is two matrices that are 41-by-37, containing only numbers. I started data at row 4 to bypass the header information/strings. Noticing the pattern, I set it up as a loop:
No_of_matrices_expected = 4;
dataCell = cell(No_of_matrices_expected, 1);
iterations = length(dataCell)
% Initial Conditions
rowBeginning = 4;
col1 = 1; % Constant
rowEnd = rowBeginning + 40; % == 44, right before next header information
col2 = 36; % Constant
for n = 1 : iterations
dataCell{n} = dlmread('example.txt', ' ', [rowBeginning, col1, rowEnd, col2]);
rowBeginning = rowBeginning + 41 + 2; % skip previous matrix and skip header info
rowEnd = rowBeginning + 40;
end
However, I stumbled across what you stated earlier which was that there are four different stations, each with their own time stamps. So running this loop more than 4 times led to unexpected results and MATLAB crashed. The reason is that the new timestamp creates an extra row for the date. Now, you could change the loop above to compensate for this extra row, or you can make multiple for loops for each station. This will be your decision to make.
Now if you wanted to save the header information, I would recommend taking a look into textscan. You can simply use this function to pull the first column of all the data into a cell array of strings. Then you can pull out the header information that you want. Keep in mind, use fopen if you want to use textscan.
I'll let you use what I have found thus far, but let me know if you need more help.
Numbers

is a way to quickly convert long cell character data to number matrix?

Below is the sample code that describe my issue.
ff= [{'1 2 3 4 5'};{'2 2 3 4 2'};{'3 2 3 4 3'};{'4 2 3 4 4'}];
YY=[];
for i=1:length(ff)
xx=str2num(ff{i,1});
YY=[YY;xx];
end
similar to the sample code my real length of ff length is very large and it is taking longer to finish the conversion. is there a way to make it faster?
Your solution is going to be particularly slow since you keep expanding the size of YY every time through the for loop.
To optimize this, you could first convert your cell array of strings into one long string using strjoin. Then you can apply str2num to this entire string at once and reshape the result.
YY = reshape(str2num(strjoin(ff)), [], numel(ff)).'
% 1 2 3 4 5
% 2 2 3 4 2
% 3 2 3 4 3
% 4 2 3 4 4
If your version of MATLAB doesn't have strjoin, you can always replace it with sprintf
YY = reshape(str2num(sprintf('%s ', ff{:})), [], numel(ff)).';
Another option would be to convert each entry of the cell array to numbers using cellfun and num2str and then concatenate the result along the first dimension.
values = cellfun(#num2str, ff, 'UniformOutput', false);
YY = cat(1, values{:});
The first option is about twice as fast since you call num2str only once and the memory needed to store the temporary string created by strjoin is going to be less than the space required to store the same data as a numeric datatype (double).

matlab plot(x,y) of different data type

i got data (2945 * 3) of different types imported as cell array. 1st column data type has been imported as text (time e.g 1/1/1990), whereas the 2nd and 3rd columns are numbers.
so far i used cell2mat to convert to double both the 2nd and 3rd columns. Thus plot(y) works {y being either the 2nd or 3rd column data} , however i am wondering how i can handle the text data type from my 1st column in an attempt to use plot(x,y).
Any idea would be appreciated. cheers
--------sample.csv-------------
Date LAST Rt
1/27/2018 20 0.234556
1/26/2019 20.05 0.184556
1/23/2040 20.1 0.134556
1/22/1990 20.15 0.084556
1/21/1991 20.2 0.034556
1/20/1993 20.25 -0.015444
1/19/1998 20.3 -0.065444
1/16/2050 20.35 -0.115444
1/15/2030 20.4 -0.165444
--------cell array appearance------------
1 | 2 | 3
1| '1/27/2018' 20 0.234556
2| '1/26/2019' 20.05 0.184556
3| '1/23/2040' 20.1 0.134556
4| '1/22/1990' 20.15 0.084556
5| '1/21/1991' 20.2 0.034556
6| '1/20/1993' 20.25 -0.015444
7| '1/19/1998' 20.3 -0.065444
8| '1/16/2050' 20.35 -0.115444
9| '1/15/2030' 20.4 -0.165444
You could also use datenum to convert the text to a serial date number (copied from Octave command line):
>> test
test =
{
[1,1] = 1/1/2000
[1,2] = 1/2/2001
[1,3] = 10/2/2001
[1,4] = 10/3/2001
[1,5] = 10/3/2005
}
>> x_dates = cellfun('datenum',test(:,1))
x_dates =
730486 730853 731126 731127 732588
>> y = rand(size(x_dates));
>> plot(x_dates,y)
>> datetick('x','dd/mm/yyyy')
Update:
It looks like cellfun requires a function handle in MATLAB, so you probably need to do something like:
x_dates = cellfun(#datenum,test(:,1))
You could use XTick and XTickLabel. The former will set up where and how many ticks you want in your X axis (I guess that you'd want one for each X data, but you also may want to go jumping 10 by 10). The second will set the labels in those tick positions. If the Labels are less than the ticks, they will repeat, so careful with that.
Let me illustrate with an example:
x = [0 1 2 3];
y = [2 0 1 1];
plot (x, y);
yourstrings={'Banana', 'T', 'Potato', '45'};
set(gca,'XTick',x(1):x(end))
set(gca,'XTickLabel',yourstrings)
A second option would be to use text. you could put text wherever you like in the plot. Let me illustrate again. Of course I don't meant to put it "nice", but if you/d like, you could play with offsetting the positions of the texts and so on in order to get a more "beautiful" plot.
x = [0 1 2 3];
y = [2 0 1 1];
plot (x, y);
yourstrings={'Banana', 'T', 'Potato', '45'};
for ii=1:length(yourstrings)
text(x(ii),y(ii),yourstrings{ii})
end
You can create a new matrix with usable data (or rewrite your current one) by looping through your matrix(:,1) to convert the strings using the cellfun function.
Or just to plot:
plot(cellfun(#(x)str2double(x), Measures(:,i)))
where i = 1: length(matrix)

How to insert comma after each element in matlab?

I have a vector with 4225 elements that its elements are separated by spaces and I have to use this vector in MuPAD as edge weight matrix of a directed graph. In order to make this vector accessible in MuPAD as a graph edge weight matrix, its elements should be separated by commas. Since the number of elements is huge, it's a waste of time to write commas between them one by one. So is there any simple way to do this in matlab?
Big thanks in advance
This ought to do the trick:
%// example vector
a = [4 5 6 7 8 9 10 11 12 13];
%// replace all consecutive spaces with a comma
aCSV = regexprep(num2str(a,17), '\s*', ',')
Output:
aCSV =
4,5,6,7,8,9,10,11,12,13
Here's a version using only sprintf:
v = [1 2 exp(1) 3 pi 4 5 realmax];
s = sprintf('%.17g,',v); % Up to 17 decimal places (double precision has about 16)
s = s(1:end-1); % Remove trailing comma
This returns
s =
1,2,2.7182818284590455,3,3.1415926535897931,4,5,1.7976931348623157e+308
See this article for details on using format strings with sprintf if you wish to further customize this.

How I get only few specified columns from a text file in Matlab?

I have a text file with 20 columns(columns are seperated by |) and many rows. How can I read only the columns 5,9,17 ?
If you want to read a file like this (called text.txt in my example)
1 | 2 | 3 | 4
2 | 3 | 4 | 5
3 | 4 | 5 | 6
just do
matrix = dlmread('text.txt');
which gives you
1 2 3 4
2 3 4 5
3 4 5 6
You can then use standard matlab matrix notation to extract for example columns 1 and 4
col1 = matrix(:, 1) % the colon is used to tell matlab to take all rows
col4 = matrix(:, 4)
You will have to form another variable choosing specific columns from the variable array formed by the import of the text file
With the right input parameters, textscan can pull this off:
Ncols = 20;
colExtract = [5 9 17];
fspec = cell(1,Ncols);
fspec(:)={'%*f '}; % the asterisk tells textscan to ignore the column
fspec(colExtract)={'%f '};
fspec{end}=fspec{end}(1:end-1); % removes the space from the last parameter
fspecstr = horzcat(fspec{:});
fid = fopen(filename);
indata = textscan(fid,fspecstr,'HeaderLines',1,'delimiter','\t');
fclose(fid);
col5 = indata{1};
col9 = indata{2};
col17= indata{3};
As you can see, I assumed there was a single headerline and the data is tab delimited. If your application does not have this, change it of course.
I guess it pays of if you're working with huge files from which you only want a small portion or can't have all the content in memory.