I try to import data from a text file to MATLAB, which has the following structure:
** Porosity
**
*POR *ALL
0.1500 0.0900 2*0.1300 0.1400 4*0.1500 0.2200 2*0.1500 0.0500
0.0900 0.1400 5*0.1500 0.2300 0.2600 0.0800 0.1500 0.1500 0.2400 0.1700
[...]
The header has to be ignored obviously. Space is the delimiter, while * indicates that the same value occurs several times as indicated by the integer before the *.
Unfortunately, the number of entries per line varies. Ideally I want to store all values in one array like this:
por = [0.1500 0.0900 0.1300 0.1300 0.1400 0.1500 0.1500 0.1500 0.1500 0.1500 0.2200 0.1500 0.1500 ...]
Can this be solved with the textscan command somehow? The file is rather large with some hundred thousand values, so I need a quick solution ;) Help is greatly appreciated!
Straight forward way (I did not use Matlab for a long period of time, so it might be not the best solution)
fid = fopen('temp.txt');
data = textscan(fid, '%s', 'delimiter', ' ');
fclose(fid);
out = convert_cells(data);
And function
function out = convert_cells(cells)
out = [];
for i = 1 : size(cells{1})
tmp = strsplit(cells{1}{i}, '*');
num1 = str2double(tmp(1));
if size(tmp, 2) == 2 && ~isnan(num1)
num2 = str2double(tmp(2));
if ~isnan(num2)
out = [out repmat(num2, 1, num1)];
end;
elseif size(tmp, 2) == 1 && ~isnan(num1)
out(end + 1) = num1;
end;
end;
end
Related
We are given four points, assumed to be ordered:
A = sort(randn(1,4))
I want to find the maximum possible number x in the interval 0<x<1 such that
A(1)<x<A(2) or A(3)<x<A(4)
Some examples:
A = [-1.4924 0.3004 1.6630 2.1204], x = 0.3004
A = [-0.4754 0.1353 0.6552 1.3873]; x = 1.0000
A = [-1.0213 -0.4521 -0.0905 0.1000]; x = 0.1000
A = [-1.8258 -0.5790 -0.4568 -0.1950]; x = 0.0000
A = [ 1.5000 2.0000 2.5000 3.0000]; x = 1.0000
Can you suggest a compact code to do this job, without having to list all the possible scenarios using if statements?
Having tried to do this with no if statements, I found that the readability of the code was greatly diminished. Note that there is only a single if statement in the code below while several other if statements could be substituted for the logical comparisons.
All of your tests pass and the code remains very concise (9 lines without the comments and loop over all of the tests).
A = [[-1.4924 0.3004 1.6630 2.1204];
[-0.4754 0.1353 0.6552 1.3873];
[-1.0213 -0.4521 -0.0905 0.1000];
[-1.8258 -0.5790 -0.4568 -0.1950];
[ 1.5000 2.0000 2.5000 3.0000]];
for i = 1:size(A,1)
% Reshape A so that each set of 2 entries are compared
Atmp = reshape(A(i,:),2,2);
% Find any valid entries
Valid = Atmp > 0 & Atmp < 1;
Ind_Valid = find(Valid == 1);
if (~isempty(Ind_Valid))
% If there are valid entries, return:
% max(A(ind),0) if the entry is the 1st of the pair
% max(A(ind),1) if the entry is the 2nd of the pair
max_Ind = max(Ind_Valid);
x = max(Atmp(max_Ind),mod(max_Ind,2))
else
% If there are no valid entries, return:
% 0 if max A < 0
% 1 if max A > 1
x = max(Atmp(:)) > 0
end
end
Output:
x =
0.3004
x =
1
x =
0.1000
x =
0
x =
1
I wanted to be:
arr(2,1) = arr(2,1) + abs(5.0 minus 5.1);% where I(1,a) is 5.0 and I(1,a+1)is 5.1
Why couldn't I add the below two together? I got this error message:
Subscripted assignment dimension mismatch (size [1 x 1] ~= size [1 x :?]).
Function 'MATLAB Function' (#620.435.485), line 23, column 2:
"arr(count,1) = arr(count,1) + abs(I(1,a)-I(1,a+1))"
Launch diagnostic report.
'I' is declared in worskspace as:
I =
5.0000 5.1000 5.2000 5.2000
90.0000 85.0000 80.0000 20.0000
integ_signal=zeros(5,4);
a=zeros(10);
arr=zeros(5,4);
count=2;
a=1;
integ_signal(count,1)= integ_signal(count-1,1);
arr(count,1) = integ_signal(count,1);
arr(count,1) = arr(count,1) + abs(I(1,a)-I(1,a+1));
The system is shown here. The complete code is below:
function integ_signal= fcn(I,V,count,oldval)
integ_signal=zeros(5,1);
a=zeros(10);
arr=zeros(2,4);
%b=zeros(10);
integ_signal=oldval;
coder.extrinsic('load');
load('data.mat','I');
a=1;
%b=1;
if count==1
integ_signal(count,1) = 10; % Initial Condition
end
if count>1
integ_signal(count,1)= integ_signal(count-1,1);
arr(count,1) = integ_signal(count,1);
arr(count,1) = arr(count,1) + abs(I(1,a)-I(1,a+1));
end
a=a+1;
% b++;
end
Dear Sir, I keep getting this error, my desired output is to get abs(I(2,ii(a))-I(2,ii(a)-1) so that when ii(a)=2, I want I(2,2) minus I(2,1) which is 5.1 minus 5.0 : Index expression out of bounds. Attempted to access element 2. The valid range is 1-1.
"2"
. at this line:
arr(1,ii(a)) = arr(1,ii(a)) + abs(I(2,ii(a))-I(2,ii(a)-1));.
I define I as the following in .mat file:
save data -v7.3 'I'
load('data.mat','I');
I =
Columns 1 through 6
0 1.0000 2.0000 3.0000 4.0000 5.0000
5.0000 5.1000 5.2000 5.2000 5.5000 5.9000
Column 7
6.0000
6.0000
I guess you call your code with count as a vector. Also, it seems like you're missing a loop. (If not, why the a = a + 1?)
a = zeros(10);
followed by
a = 1;
does not make much sense. What is your input variable I? What do you want load('data.mat','I') to do?
If I'm correct, try something like:
for ii = 1:length(count)
if count(ii) == 1
integ_signal(count(ii),1) = 10; % Initial Condition
end
if count(ii) > 1
integ_signal(count(ii),1)= integ_signal(count(ii)-1,1); % Or, (count(ii-1),1)
arr(count(ii),1) = integ_signal(count(ii),1);
arr(count(ii),1) = arr(count(ii),1) + abs(I(1,a)-I(1,a+1));
end
a = a + 1;
end
You probably need to tweak this in some way, but I think it may help you a bit.
I keep getting this error:
Index expression out of bounds. Attempted to access element 2. The
valid range is 1-1."2".
My desired output is to get
abs(I(2,ii(a))-I(2,ii(a)-1)
so that when ii(a)=2, I want I(2,2) minus I(2,1) which is 5.1 minus 5.0
The error is due to this line:
arr(1,ii(a)) = arr(1,ii(a)) + abs(I(2,ii(a))-I(2,ii(a)-1));.
I define I as the following in .mat file:
save data -v7.3 'I'
load('data.mat','I');
I =
Columns 1 through 6
0 1.0000 2.0000 3.0000 4.0000
5.0000 5.1000 5.2000 5.2000 5.5000
The code:
function arr= fcn(I,count,oldval)
persistent integ_signal
if isempty( integ_signal)
integ_signal=zeros(1,5)
end
persistent a
if isempty(a)
a=zeros(1)
end
arr=zeros(1,5);
ii=zeros(1,5);
aa=zeros(2,5);
integ_signal=oldval;
coder.extrinsic('load');
aa=load('data.mat','I');
if count==1
a=1;
ii(a)=count;
integ_signal(1,ii(a)) = 10; % Initial Condition
end
if count ~= 1
a=count;
ii(a)=count;
integ_signal(1,ii(a))= integ_signal(1, ii(a)-1);
arr(1,ii(a)) = integ_signal(1,ii(a));
arr(1,ii(a)) = arr(1,ii(a)) + abs(aa(2,ii(a))-aa(2,ii(a)-1));
end
a = a + 1;
end
I want to split a matrix columnwise into 3 segments and do a calculation on it (mean()). Is there a way to get this without a for-loop, as I did in this provided sample?
M = [2 4 9; 50 50 200; 30 0 0];
M = [M 10*M]
N = length(M);
seg = 3 % split in lets say 3 parts
segLen = round(N/seg)
segBeg = (((1:seg)-1) * segLen)+1 % start indices
segEnd = segBeg + segLen -1 % end indices
for i = 1: length(segBeg)
mean(M(:,segBeg(i):segEnd(i)),2)
end
Thank you!
Think outside the box: use the 3rd dimension:
r=reshape(M,size(M,1),segLen,[])
squeeze(mean(r,2))
The first line produces a 3d array with the first matrix at r(:,:,1), the second at r(:,:,2), ... (use M(:,1:seg*segLen) instread of M if the number of columns is not divisible by segLen).
mean(r,2) produces a nrows-by-1-by-seg array, squeeze makes a nrows-by-seg matrix out of it again.
You can use arrayfun together with cell2mat
result = cell2mat(arrayfun(#(x,y) mean(M(:,x:y),2), segBeg, segEnd,...
'UniformOutput', false))
This results in
result =
1.0e+03 *
0.0030 0.0145 0.0650
0.0500 0.3500 1.2500
0.0150 0.1500 0
where each column represents the mean across one submatrix.
Another solution using blockproc (like suggested by #DennisJaheruddin in the comments) could look like this
myFun = #(x) mean(x.data,2);
result2 = blockproc(M, [N, segLen], myFun)
This also results in
result2 =
1.0e+03 *
0.0030 0.0145 0.0650
0.0500 0.3500 1.2500
0.0150 0.1500 0
Note that blockproc can take advantage of parallel processing if the flag 'UseParallel' is set to true, i.e., result2 = blockproc(M, [N, segLen], myFun, 'UseParallel', true)
You can do for your example case
mean1 = mean(M(:,1:segLen))
mean2 = mean(M(:,segLen+1:N-segLen-1))
mean3 = mean(M(:,N-segLen:end))
I have a data file look like the following
3 1.0 1.4 1.7
2 1.2 1.5
1 1.1
2 1.1 1.2
For each line, the first integer indicates the number of floating numbers in this line.
Now I want to load all the data into a single matlab array, and ignore first column, that is, I want to get a array like this
>>arr = [1.0, 1.4, 1.7, 1.2, 1.5, 1.1, 1.1, 1.2]
if for each line, we have same number of floating numbers, I can simply do it like this
>>arr = load datafile ;
>>arr = arr(:,2:end) ; %ignore the first column
>>arr = arr(:) ;
However, if we have different number of floating numbers in each line, it seems we cannot directly loaded the file into a matrix. Is there any simple way to accomplish this ?
Thank you.
First, let's read the numbers as strings:
C = textread('myfile.txt', '%s', 'delimiter', '\n');
The result is a cell-array of strings, so let's apply str2num on each cell to obtain numerical values:
C = cellfun(#str2num, C, 'Uniform', false);
Now let's discard the first element from each cell:
C = cellfun(#(x)x(2:end), C, 'Uniform', false);
Finally, we concatenate all values into one vector:
arr = [C{:}]
This is the complete code:
C = textread('test.txt', '%s', 'delimiter', '\n'); %// Read data
C = cellfun(#str2num, C, 'Uniform', false); %// Convert to numbers
C = cellfun(#(x)x(2:end), C, 'Uniform', false); %// Remove first values
arr = [C{:}]
arr =
1.0000 1.4000 1.7000 1.2000 1.5000 1.1000 1.1000 1.2000
An easy way to do this would be to just read the file line by line
fid = fopen('data.txt');
arr = [];
tline = fgetl(fid);
while (tline ~= -1)
temp = str2num(tline);
arr = [arr temp(2:end)];
tline = fgetl(fid);
end
You might also try using the loadcell function, though I didn't try it so I'm not positive it will work for you.