I'm writing a matlab class which uses data from a .mat-file.
Is there any way I can place this data file inside the class directory structure and make it accessible only to my class?
I.e. I have a directory structure like this:
./MATLAB
+myPackage
#MyClass
MyClass.m
where MyClass.m looks like
classdef MyClass
methods
function y = getValue(obj, x)
load datafile.mat
y = interp1(datax, datay, x);
end
end
end
This works great as long as datafile.mat is in my working directory, but I'd like to move it to +myPackage/#MyClass/datafile.mat instead (or better yet +myPackage/datafile.mat). How would I make my class(es) find it there?
First, you may put the data file in a 'private' subfolder inside your directory structure:
./MATLAB
+myPackage
private
datafile.mat
#MyClass
MyClass.m
NB: This won't garantee that it is only accessible to your class, anyway it will be classified close to your class definition.
Then, you can find back data values like this in your method:
classdef MyClass
methods
function y = getValue(obj, x)
% Find back datafile name
localPath = fileparts(mfilename('fullpath'));
dataFilename = fullfile(localpath, '..', 'private', 'datafile.mat'); % '..' is to go up one folder, up to you to place th file somewhere else (e.g. under +MyPackage directly)
% Read file
data = load(dataFilename);
datax = data.datax;
datay = data.datay;
y = interp1(datax, datay, x);
end
end
end
EDIT: Example with Resources class
Here is an example with separate resource class to avoid loading file data each time getValue is called. I also added for the case you need to care if the file has been modified or not:
classdef Resources
methods(Static)
function [resource] = GetResource(name, careForUpdates)
%[
if (nargin < 2), careForUpdates = true; end
if (nargin < 1), error('Not enough input arguments.'); end
persistent cache;
if (isempty(cache)), cache = containers.Map('UniformValues', false); end
filename = Resources.GetFileName(name);
lastModified = dir(filename);
lastModified = datenum(lastModified.date);
if (~cache.isKey(name) || ...
(careForUpdates && (cache(name).LastModified < lastModified)))
data.LastModified = lastModified;
data.Value = load(filename);
cache(name) = data;
end
resource = cache(name).Value;
%]
end
end
methods(Static, Access=private)
function [fname] = GetFileName(name)
%[
persistent cache;
if (isempty(cache)), cache = containers.Map('UniformValues', false); end
if (~cache.isKey(name))
localpath = fileparts(mfilename('fullpath'));
switch(name)
case 'titi', cache(name) = fullfile(localpath, 'titi.mat');
case 'toto', cache(name) = fullfile(localpath, 'toto.mat');
case 'tata', cache(name) = fullfile(localpath, 'tata.mat');
otherwise, error('Resource `%s` is unknown', name)
end
end
fname = cache(name);
%]
end
end
end
This can be used like this
data = Resources.Get('titi');
Or, if not interested by file updates:
careForFileUpdates = false;
data = Resources.Get('titi', careForFileUpdates);
Related
Maybe it's a trivial question but I would like to know how to access a constant property within the class contructor or a class function in octave. Let's make an example:
classdef Example % < FatherClass
% read-only protected properties
properties (Constant=true, Access=protected)
all_levels = {"A","B","C","D"};
endproperties
% protected properties
properties(Access=protected)
level = 'D';
output = '.';
endproperties
methods(Access=public)
function obj = Example (level,outputfilepath)
if any(strcmp(all_levels,level))
obj.level = level;
else
error ("possible levels are: A B C D");
endif
obj.output = outputfilepath
endfunction
endmethods
end
running this class example I receive the error:
error: 'all_levels' undefined near line 12, column 12
error: called from
Example at line 12 column 13
So, I've tried something like
if any(strcmp(obj.all_levels,level))
obj.level = level;
With the same result, also defining a getter:
methods (Static = true)
function lvs = gel_levels()
lvs = all_levels
endfunction
endmethods
...
methods(Access=public)
function obj = Example (obj,level,outputfilepath)
all_levels = get_levels()
% disp(all_levels)
if any(strcmp(all_levels,level))
obj.level = level;
else
error ("possible levels are: A B C D");
endif
obj.output = outputfilepath
endfunction
endmethods
Sorry but I'm quite new to octave and I haven't found any example about this. What I'm trying to accomplish is a simple class variable
The question is a bit confusing, as different attempts seem to be using different parts, but in general I think your problem is that you are not passing the object as a formal parameter in the method.
It is also not clear if you are trying to modify the object "in-place", or trying to generate a new one ... but in any case remember that modifying objects in place is not possible (unless inheriting from the 'handle' object). Therefore the typical thing you're supposed to do is: pass the object in as the first input as you're supposed to do with class method definitions, modify it, return it, and then when you're using this method in your calling workspace, capture this object (typically in a variable by the same name as the called object in the calling workspace) via assignment.
This works for me:
%% in Example.m
classdef Example
% read-only protected properties
properties( Constant=true, Access=protected )
all_levels = {"A", "B", "C", "D"};
endproperties
% protected properties
properties( Access = protected )
level = 'D';
output = '.';
endproperties
methods( Access = public )
function obj = Logging( obj, level, outputfilepath )
valid_level_choice = any( strcmp( obj.all_levels, level ) );
if valid_level_choice, obj.level = level;
else, error( "possible levels are: A B C D" );
endif
obj.output = outputfilepath;
endfunction
function get_level( obj )
fprintf( "The level is %s\n;", obj.level );
endfunction
endmethods
endclassdef
%% In your console session
E = Example();
E.get_level()
%> The level is D
E = E.Logging( 'A', './' );
E.get_level()
%> The level is A
UPDATE
Updated code given the revised question / comments.
This works for me in octave 7.1.0
%% in Example.m
classdef Example % < FatherClass
% read-only protected properties
properties (Constant=true, Access=protected)
all_levels = {"A","B","C","D"};
endproperties
% protected properties
properties(Access=protected)
level = 'D';
output = '.';
endproperties
methods(Access=public)
% Constructor
function obj = Example (level,outputfilepath)
valid_choice = any(strcmp(obj.all_levels,level));
if valid_choice, obj.level = level;
else, error ("possible levels are: A B C D");
endif
obj.output = outputfilepath;
endfunction
% Remaining Methods
function get_level( obj ), fprintf( "The level is %s\n", obj.level ); endfunction
function change_all_levels( obj, C ), obj.all_levels = C; endfunction
endmethods
end
%% octave session
octave:1> E = Example('A', '.');
octave:2> E.get_level()
%> The level is A
octave:3> E.change_all_levels( {'this', 'should', 'not', 'work' } );
%> error: subsasgn: cannot assign constant property: all_levels
%> error: called from
%> change_all_levels at line 25 column 66
I have a text file with data structure like this
30,311.263671875,158.188034058,20.6887207031,17.4877929688,0.000297248129755,aeroplane
30,350.668334961,177.547393799,19.1939697266,18.3677368164,0.00026999923648,aeroplane
30,367.98135376,192.697219849,16.7747192383,23.0987548828,0.000186387239864,aeroplane
30,173.569274902,151.629364014,38.0069885254,37.5704650879,0.000172595537151,aeroplane
30,553.904602051,309.903320312,660.893981934,393.194030762,5.19620243722e-05,aeroplane
30,294.739196777,156.249740601,16.3522338867,19.8487548828,1.7795707663e-05,aeroplane
30,34.1946258545,63.4127349854,475.104492188,318.754821777,6.71026540999e-06,aeroplane
30,748.506652832,0.350944519043,59.9415283203,28.3256549835,3.52978979379e-06,aeroplane
30,498.747009277,14.3766479492,717.006652832,324.668731689,1.61551643174e-06,aeroplane
30,81.6389465332,498.784301758,430.23046875,210.294677734,4.16855394647e-07,aeroplane
30,251.932098389,216.641052246,19.8385009766,20.7131652832,3.52147743106,bicycle
30,237.536972046,226.656692505,24.0902862549,15.7586669922,1.8601918593,bicycle
30,529.673400879,322.511322021,25.1921386719,21.6920166016,0.751171214506,bicycle
30,255.900146484,196.583847046,17.1589355469,27.4430847168,0.268321367912,bicycle
30,177.663650513,114.458488464,18.7516174316,16.6759414673,0.233057001606,bicycle
30,436.679382324,273.383331299,17.4342041016,19.6081542969,0.128449092153,bicycle
I want to index those file with a label file.and the result will be something like this.
60,509.277435303,284.482452393,26.1684875488,31.7470092773,0.00807665128377,15
60,187.909835815,170.448471069,40.0388793945,58.8763122559,0.00763951029512,15
60,254.447280884,175.946624756,18.7212677002,21.9440612793,0.00442053096776,15
However there might be some class that is not in label class and I need to filter those line out so I can use load() to load in.(you can't have char inside that text file and execute load().
here is my implement:
function test(vName,meta)
f_dt = fopen([vName '.txt'],'r');
f_indexed = fopen([vName '_indexed.txt'], 'w');
lbls = loadlbl()
count = 1;
while(true),
if(f_dt == -1),
break;
end
dt = fgets(f_dt);
if(dt == -1),
break
else
dt_cls = strsplit(dt,','){7};
dt_cls = regexprep(dt_cls, '\s+', '');
cls_idx = find(strcmp(lbls,dt_cls));
if(~isempty(cls_idx))
dt = strrep(dt,dt_cls,int2str(cls_idx));
fprintf(f_indexed,dt);
end
end
end
fclose(f_indexed);
if(f_dt ~= -1),
fclose(f_dt);
end
end
However it work very very slow because the text file contains 100 thousand of lines. Is it anyway that I could do this task smarter and faster?
You may use textscan, and get the indices/ line numbers of the labels you want. After knowing the line numbers, you can extract what you want.
fid = fopen('data.txt') ;
S = textscan(fid,'%s','delimiter','\n') ;
S = S{1} ;
fclose(fid) ;
%% get bicycle lines
idx = strfind(S, 'bicycle');
idx = find(not(cellfun('isempty', idx)));
S_bicycle = S(idx)
%% write this to text file
fid2 = fopen('text.txt','wt') ;
fprintf(fid2,'%s\n',S_bicycle{:});
fclose(fid2) ;
From S_bicycle, you can extract your numbers.
First, sorry for the bad title - I'm new to OO programming - basically I'd like to understand how Matlab works with oop classes.
Before I ask my question, here is a basic example of what I want to do:
I have two houses and some data about them and I got the idea, to work with oop classes. Here is my .m file.
classdef building
properties
hohe = 0;
lange = 0;
breite = 0;
xabstandsolar = 0;
yabstandsolar = 0;
end
methods
function obj = building(hohe, lange, breite, xabstandsolar, yabstandsolar)
obj.hohe = hohe;
obj.lange = lange;
obj.breite = breite;
obj.xabstandsolar = xabstandsolar;
obj.yabstandsolar = yabstandsolar;
end
function hohenwinkel(h)
h = h
d = sqrt(obj.xabstandsolar^2 + yabstandsolar^2);
gamma_v = atand((obj.hohe - h)/(d));
end
end
end
I filled it with some data - for example
>>H1 = building(10,8,6,14,8)
>>H2 = building(18,8,6,14,0)
And now I want to calculate "gamma_v" (as an 1x2 array) for each building. Any ideas, how I can archive this?
Edit:
I want to create gamma_v (as an array) automatically for all objects in the class "building". There will be a lot more "houses" in the final script.
Your hohenwinkel method needs to accept two input arguments. The first argument for non-static methods is always the object itself (unlike C++, you'll have to explicitly list it as an input argument) and the second input will be your h variable. You'll also want to actually return the gamma_v value using an output argument for your method.
function gamma_v = hohenwinkel(obj, h)
d = sqrt(obj.xabstandsolar^2 + obj.yabstandsolar^2);
gamma_v = atand((obj.hohe - h)/(d));
end
Then you can invoke this method on each building to get the value
gamma_v1 = hohenwinkel(H1);
gamma_v2 = hohenwinkel(H2);
If you want to have an array of buildings, you can create that array
houses = [building(10,8,6,14,8), building(18,8,6,14,0)];
gamma_v = hohenwinkel(houses);
and then construct your hohenwinkel function to operate on each one and return the result
function gamma_v = hohenwinkel(obj, h)
if numel(obj) > 1
% Compute hohenwinkel for each one
gamma_v = arrayfun(#(x)hohenwinkel(x, h), obj);
return
end
d = sqrt(obj.xabstandsolar^2 + obj.yabstandsolar^2);
gamma_v = atand((obj.hohe - h)/(d));
end
there is some tricky solution (and its not recommended)(#Suever solution is better)
you should create a handle class
classdef gvclass<handle
properties
gvarr=[];
end
methods
function setgvarr(obj,value)
obj.gvarr=[obj.gvarr,value];
end
end
end
then use this class in your building class
classdef building<handle
properties
gv
hohe = 0;
lange = 0;
breite = 0;
xabstandsolar = 0;
yabstandsolar = 0;
end
methods
function obj = building(hohe, lange, breite, xabstandsolar, yabstandsolar,handle_of_your_gv_class,h)
obj.hohe = hohe;
obj.lange = lange;
obj.breite = breite;
obj.xabstandsolar = xabstandsolar;
obj.yabstandsolar = yabstandsolar;
obj.gv=handle_of_your_gv_class;
obj.hohenwinkel(h);
end
function hohenwinkel(obj,h)
d = sqrt(obj.xabstandsolar^2 + yabstandsolar^2);
gamma_v = atand((obj.hohe - h)/(d));
obj.gv.setgvarr(gamma_v);
end
end
end
finally before creating any building you should create an object of gv class and pass it to the building constructor,
Egv=gvclass();
H1 = building(10,8,6,14,8,Egv,2)
H2 = building(18,8,6,14,0,Egv,3)
and to access gv array:
Egv.gvarr
you should do more effort on this issue to debug possible errors.
I am given a structure with variable names L1dirs, L2dirs...etc all the way up to however many levels the user wants. Each Lxdirs contains a cell array of the names of the directories to be created.
The end result should be the creation of a nested set of directories where each level 1 directory contains all level 2 directories and all level 2 directories contain all level 3 directories, etc. How can I dynamically create this hierarchy?
From the code below, I have already found out through try-catch statements how many levels the user specified.
Now given that we know how many levels the user specified, how can we generate a list of all the unique filepath combinations? The end result should be a column cell array of m paths where mis the number of L1 directories times the number of L2 directories times.....times the number of Lx directories.
Can MATLAB do this? I attempted to use eval() by creating a dynamically created string macro, but eval doesn't like the use of the end statement when trying to dynamically nest for loops. Is there another way?
Here is a sample piece of code of what I have so far:
Main Code
userinputs.L1dirs = {'Level 1 Dir 1';
'Level 1 Dir 2';
'Level 1 Dir 3'};
userinputs.L2dirs = {'Level 2 Dir 1';
'Level 2 Dir 2';
'Level 2 Dir 3'};
userinputs.L3dirs = {'Level 3 Dir 1';
'Level 3 Dir 2';
'level 3 Dir 3'};
userinputs.top_level_dir = strcat(pwd,'\Results\');
pathlist1 = check_results_dirs(userinputs)
userinputs.L4dirs = {'Level 4 Dir 1';
'Level 4 Dir 2'};
userinputs.top_level_dir = strcat(pwd,'\Results 2\');
pathlist2 = check_results_dirs(userinputs)
Support Function
function pathlist = check_results_dirs(inputdata)
%{
This function checks if the file directory exists in the top level
directory as specified by the inputs structure. If the directory already
exists, then it checks whether these files are to be overwritten or not.
This function dynamically checks how many levels of directories the user
specified.
Inputs
inputdata - structure containing the following variable names
inputdata.LXdirs - X is an integer value. Variable(s) contain cell
arrays of directory names
inputdata.top_level_dir - top level destination directory to
create this file structure. If this folder does not exist, it will be
created.
%}
%check if top level directory exists
if ~exist(inputdata.top_level_dir,'file')
mkdir(inputdata.top_level_dir);
end
%determine how many directory levels there are as determined by the user
numDirLevels = 1;
numDirsPerLevel = [];
moreDirsFlag = 1;
while moreDirsFlag
try
eval(sprintf('temp = inputdata.L%idirs;',numDirLevels));
numDirsPerLevel = [numDirsPerLevel; length(temp)];
numDirLevels = numDirLevels + 1;
catch err
if strcmp(err.identifier,'MATLAB:nonExistentField')
%no more directory levels
numDirLevels = numDirLevels - 1;
moreDirsFlag = 0;
else
rethrow(err);
end
end
end
numUniqueDirs = prod(numDirsPerLevel);
%Generate Path list
beginstr = '';
midstr = 'pathlist{numUniqueDirs} = strcat(';
endstr = '';
for ii = 1:numDirsPerLevel
beginstr = strcat(beginstr,sprintf('for x%i=1:numDirsPerLevel(%i) ',ii,ii));
midstr = strcat(midstr,sprintf('inputdata.L%idirs(x%i),''\\'',',ii,ii));
endstr = strcat(' end ',endstr);
end
midstr = strcat(midstr,''''');');
evalstr = ' numUniqueDirs = numUniqueDirs+1;'
midstr = strcat(midstr,evalstr);
evalstr = 'numUniqueDirs = 1; '
beginstr = strcat(evalstr,beginstr);
eval(strcat(beginstr,midstr,endstr)); %error is thrown here due to an illegal
%use of 'end'. Can I not
%use for loops using eval?
%debug statements
disp(beginstr)
disp(midstr)
disp(endstr)
end
Note how in the main code the function is called twice. Once calls it to make three levels of directories another call is to make four levels of directories. The output pathlist should contain numL1dirs times numL2dirs times .... times numLxdirs so for the first example it should create 27 distinct directories such that each level 1 dir contains all three level 2 dirs and each level 2 dir contains all three level 3 dirs. For the second example, pathlist should contain 54 unique directory paths.
Well the reason why you struggled so much is probably your attachment to eval. You need to really limit the use of that to a strict minimum ... ideally never. I can see why you thought you had to resort to it (your dynamic field/variable names) but fortunately Matlab gave us a few tools to overcome that.
You should have a good look at the Dynamic field names functionalities from Matlab. This will probably be of great use given your style of programming, and use it as much as you can to avoid any use of the evil eval.
Once you use this. Do a recursive call to generate a list of strings representing all the paths of the folders to generate, then mkdir them and you're done.
function pathList = Generate_PathList(userinputs)
%% // check if top level directory exists
if ~exist(userinputs.top_level_dir,'dir')
mkdir(userinputs.top_level_dir);
end
%% // determine how many directory levels there are
fnames = fieldnames(userinputs) ;
maxDirLevel = 1 ;
for iField = 1:numel(fnames)
lvlDir = sscanf(fnames{iField},'L%idirs') ;
if lvlDir > maxDirLevel
maxDirLevel = lvlDir ;
end
end
%% // Generate string list
pathList = { userinputs.top_level_dir } ;
for iLvl = 1:maxDirLevel % // loop on all directory level
dirLevelName = ['L' num2str(iLvl) 'dirs'] ;
pathList = addPathToList( pathList , userinputs.(dirLevelName) ) ;
end
%% // Generate the directories
for iDir = 1:numel(pathList)
mkdir( pathList{iDir} ) ;
end
%% // Helper function called recursively
function pathListOut = addPathToList( pathList , dirNames )
nTop = numel(pathList) ;
nLow = numel(dirNames) ;
pathListOut = cell( nTop*nLow , 1 ) ;
iPath = 0 ;
for iTopDir = 1:nTop
for iLowDir = 1:nLow
iPath = iPath + 1 ;
pathListOut{iPath} = [pathList{iTopDir} '\' dirNames{iLowDir}] ;
end
end
You can call this function the same way you used to call your initial function:
userinputs.top_level_dir = strcat(pwd,'\Results');
pathList = Generate_PathList(userinputs)
userinputs.L4dirs = {'Level 4 Dir 1';
'Level 4 Dir 2'};
userinputs.top_level_dir = strcat(pwd,'\Results 2');
pathList = Generate_PathList(userinputs)
Will create your 27 and respectively 54 unique folders.
Just note that I removed the last '\' in your top level folder definition (to be consistent with the folder path generation used later on ...)
I've got a folder which contains subfolders with text files. I want to read those file with the same order as they are in the subfolders. I've got a problem with that. I use the following matlab code:
outNames = {};
k=1;
feature = zeros(619,85);
fileN = cell(619,1);
for i=1:length(nameFolds)
dirList = dir(strcat(path, num2str(cell2mat(nameFolds(i,1)))));
names = {dirList.name};
outNames = {};
for j=1:numel(names)
name = names{j};
if ~isequal(name,'.') && ~isequal(name,'..')
[~,name] = fileparts(names{j});
outNames{end+1} = name;
fileName = strcat(path, num2str(cell2mat(nameFolds(i,1))), '\', name, '.descr' );
feature(k,:) = textread(fileName);
fileN{k} = [fileName num2str(k)];
k= k+1;
end
end
end
In one subfolder I've got the above text file names:
AnimalPrint_tiger_test_01.descr
AnimalPrint_tiger_test_02.descr
AnimalPrint_tiger_test_03.descr
AnimalPrint_tiger_test_04.descr
AnimalPrint_tiger_test_05.descr
AnimalPrint_tiger_test_06.descr
AnimalPrint_tiger_test_07.descr
AnimalPrint_tiger_test_08.descr
AnimalPrint_tiger_test_09.descr
AnimalPrint_tiger_test_10.descr
AnimalPrint_tiger_test_11.descr
AnimalPrint_tiger_test_12.descr
AnimalPrint_tiger_test_13.descr
AnimalPrint_tiger_test_14.descr
AnimalPrint_tiger_test_15.descr
AnimalPrint_zebra_test_1.descr
AnimalPrint_zebra_test_2.descr
AnimalPrint_zebra_test_3.descr
AnimalPrint_zebra_test_4.descr
AnimalPrint_zebra_test_5.descr
AnimalPrint_zebra_test_12.descr
But it seems that it reads first the AnimalPrint_zebra_test_12.descr and after the AnimalPrint_zebra_test_1.descr and the rest. Any idea why this happens?
dir sorts the files according to their names, for instance
test_1
test_12 % 1 followed by 2
test_2
test_3
You may want to build your own order with ['test_' num2str(variable) '.descr'] that concatenates test_ with an incrementing variable.