I've made an M-file that outputs data into my MATLAB command window in the form below (I've deleted the extra spaces). Is there an easy way to convert this output into an array, without having to type all the data into the array editor as I'm currently doing? (Or even run it straight from the M-file into an array?)
T = 0.3000
price = 24.8020
T = 0.4000
price = 28.3453
T = 0.5000
price = 31.3934
T = 0.6000
price = 34.0880
Organize your data into arrays.
For example:
T=0.3:0.1:0.6;
Price=yourfunction(T);
Then if you want a price vs T graph,
plot(T,Price)
If you've got a large amount of data, try to avoid for loops, as they're slower than vectorized code.
At some point in your M-file you are printing each line of data to the command window, presumably using DISP or FPRINTF. You can replace that line with the following:
data = [data; T price];
Where T and price are the variables holding your data. Every time you call the above line (say, in a loop) it will append your data as a new row to the variable data. At some point at the beginning of your M-file, you would therefore have to add the following initialization:
data = []; %# An empty array
Appending values to an array like this can sometimes be inefficient, so if you already know ahead of time how many rows of data you will collect you can instead preallocate data with a given size. For example, if you know you will have 4 pairs of values for T and price, you can initialize data in the following way:
data = zeros(4,2); %# A 4-by-2 array of zeroes
Then, when you add data to the array you would instead do the following:
data(i,:) = [T price]; %# Fill row i with data
Another issue to consider is whether your M-file is a script or a function. A function M-file has a line like function output = file_name(input) at the top, whereas a script M-file does not. Running a script M-file is equivalent to typing the entire contents of the file directly into the MATLAB command window, so all of the variables created in the M-file are available in the workspace.
If you are using a function M-file, all variables created are local to the function, and any you want to use in the workspace will have to be passed as outputs from the function. For example, the top line of your function M-file could look like:
function data = your_file
where your_file is the name of the M-file and data is a variable being returned. When you call this function from the workspace you would then have to capture the output in a variable:
outputData = your_file();
Now you have the contents of the variable data from your_file stored as a new variable outputData in the workspace.
Why do you print out data instead of collecting it in an array?
M = [];
for ...
M(end+1, :) = [T, price];
end;
or, more idiomatically,
M = 0.3:0.1:0.6; % or whatever your T values should be
M = [M' (M'.^2)] % replace the .^2 by your price function
Related
I am using a list of integers corresponding to an x,y index of a gridded NetCDF array to extract specific values, the initial code was derived from here. My NetCDF file has a single dimension at a single timestep, which is named TMAX2M. My code written to execute this is as follows (please note that I have not shown the call of netCDF4 at the top of the script):
# grid point lists
lat = [914]
lon = [2141]
# Open netCDF File
fh = Dataset('/pathtofile/temperaturedataset.nc', mode='r')
# Variable Extraction
point_list = zip(lat,lon)
dataset_list = []
for i, j in point_list:
dataset_list.append(fh.variables['TMAX2M'][i,j])
print(dataset_list)
The code executes, and the result is as follows:
masked_array(data=73,mask=False,fill_value=999999,dtype=int16]
The data value here is correct, however I would like the output to only contain the integer contained in "data". The goal is to pass a number of x,y points as seen in the example linked above and join them into a single list.
Any suggestions on what to add to the code to make this achievable would be great.
The solution to calling the particular value from the x,y list on single step within the dataset can be done as follows:
dataset_list = []
for i, j in point_list:
dataset_list.append(fh.variables['TMAX2M'][:][i,j])
The previous linked example contained [0,16] for the indexed variables, [:] can be used in this case.
I suggest converting to NumPy array like this:
for i, j in point_list:
dataset_list.append(np.array(fh.variables['TMAX2M'][i,j]))
I've managed to answer my own question. This code will write cell arrays of any shape containing strings. The datasets can be modified/overwritten by simply calling again with a different input.
https://www.mathworks.com/matlabcentral/fileexchange/24091-hdf5-read-write-cellstr-example
%Okay, Matlab's h5write(filename, dataset, data) function doesn't work for
%strings. It hasn't worked with strings for years. The forum post that
%comes up first in Google about it is from 2009. Yeah. This is terrible,
%and evidently it's not getting fixed. So, low level functions. Fun fun.
%
%What I've done here is adapt examples, one from the hdf group's website
%https://support.hdfgroup.org/HDF5/examples/api18-m.html called
%"Read / Write String Datatype (Dataset)", the other by Jason Kaeding.
%
%I added functionality to check whether the file exists and either create
%it anew or open it accordingly. I wanted to be able to likewise check the
%existence of a dataset, but it looks like this functionality doesn't exist
%in the API, so I'm doing a try-catch to achieve the same end. Note that it
%appears you can't just create a dataset or group deep in a heirarchy: You
%have to create each level. Since I wanted to accept dataset names in the
%same format as h5read(), in the event the dataset doesn't exist, I loop
%over the parts of the dataset's path and try to create all levels. If they
%already exist, then this action throws errors too; hence a second
%try-catch.
%
%I've made it more advanced than h5create()/h5write() in that it all
%happens in one call and can accept data inputs of variable size. I take
%care of updating the dataset's extent to accomodate changing data array
%sizes. This is important for applications like adding a new timestamp
%every time the file is modified.
%
%#author Pavel Komarov pavel#gatech.edu 941-545-7573
function h5createwritestr(filename, dataset, str)
%"The class of input data must be cellstring instead of char when the
%HDF5 class is VARIABLE LENGTH H5T_STRING.", but also I don't want to
%force the user to put braces around single strings, so this.
if ischar(str)
str = {str};
end
%check whether the specified .h5 exists and either create or open
%accordingly
if ~exist(filename, 'file')
file = H5F.create(filename, 'H5F_ACC_TRUNC', 'H5P_DEFAULT', 'H5P_DEFAULT');
else
file = H5F.open(filename, 'H5F_ACC_RDWR', 'H5P_DEFAULT');
end
%set variable length string type
vlstr_type = H5T.copy('H5T_C_S1');
H5T.set_size(vlstr_type,'H5T_VARIABLE');
% There is no way to check whether a dataset exists, so just try to
% open it, and if that fails, create it.
try
dset = H5D.open(file, dataset);
H5D.set_extent(dset, fliplr(size(str)));
catch
%create the intermediate groups one at a time because evidently the
%API's functions aren't smart enough to be able to do this themselves.
slashes = strfind(dataset, '/');
for i = 2:length(slashes)
url = dataset(1:(slashes(i)-1));%pull out the url of the next level
try
H5G.create(file, url, 1024);%1024 "specifies the number of
catch %bytes to reserve for the names that will appear in the group"
end
end
%create a dataspace for cellstr
H5S_UNLIMITED = H5ML.get_constant_value('H5S_UNLIMITED');
spacerank = max(1, sum(size(str) > 1));
dspace = H5S.create_simple(spacerank, fliplr(size(str)), ones(1, spacerank)*H5S_UNLIMITED);
%create a dataset plist for chunking. (A dataset can't be unlimited
%unless the chunk size is defined.)
plist = H5P.create('H5P_DATASET_CREATE');
chunksize = ones(1, spacerank);
chunksize(1) = 2;
H5P.set_chunk(plist, chunksize);% 2 strings per chunk
dset = H5D.create(file, dataset, vlstr_type, dspace, plist);
%close things
H5P.close(plist);
H5S.close(dspace);
end
%write data
H5D.write(dset, vlstr_type, 'H5S_ALL', 'H5S_ALL', 'H5P_DEFAULT', str);
%close file & resources
H5T.close(vlstr_type);
H5D.close(dset);
H5F.close(file);
end
I found a bug!
spacerank = length(size(str));
Now it works flawlessly as far as I can tell.
I have a very long lookup table (~40,000 lines) that I am using for my code. Currently, I have it set to grab 4 arrays from my lookup table in the subroutine that uses it, but I call that subroutine ~3,000 times. I would rather not waste processing time grabbing this table as arrays repeatedly. Is there a way to grab them in my main program, store them, and source them later in my subroutine?
My current code grabs the lookup table in 4 separate arrays of 39,760 lines, and I am currently calling it like this:
READCOL, 'LookupTable2.txt', F='D,D,D,D',Albedo, Inertia, NightT, DayT
EDIT: I should probably note I have IDL 6.2, but if there is a way to do it in a newer version, I would still appreciate knowing how.
EDIT 2: My current program has a function which saves 4 arrays and executes the main function. Can I call my function with arrays as an argument? That way I wouldn't have to keep creating the same array
Something like:
Pro
FUNC(Array1, Array2, Var1, Var2, Var3)
END
There are several ways you can do this.
It looks like you have four columns and 40,000 lines, correct?
Then you can do the following. First, I will assume there is no header data in the ASCII file for the following commands.
FUNCTION read_my_file,file_name
;; Assume FILE_NAME is full path to and including file name with extension
fname = file_name[0]
;; One could also find the file with the following
;; fname = FILE_SEARCH([path to file],[file name with extension])
;; Define the number of lines in the file
nl = FILE_LINES(fname[0])
;; Define empty arrays to fill
col1 = DBLARR(nl[0])
col2 = DBLARR(nl[0])
col3 = DBLARR(nl[0])
col4 = DBLARR(nl[0])
dumb = DBLARR(4)
;; Open file
OPENR,gunit,fname[0],ERROR=err,/GET_LUN
IF (err NE 0) THEN PRINT, -2, !ERROR_STATE.MSG ;; Prints an error message
FOR n=0L, nl[0] - 1L DO BEGIN
;; Read in file data
READF,gunit,FORMAT='(4d)',dumb
;; Fill arrays
col1[n] = dumb[0]
col2[n] = dumb[1]
col3[n] = dumb[2]
col4[n] = dumb[3]
ENDFOR
;; Close file
FREE_LUN,gunit
;; Define output
output = [[col1],[col2],[col3],[col4]]
;; Return to calling routine
RETURN,output
END
Note that this will work better if you provide an explicit width for the format statement, e.g., '(4d15.5), which means a 15 character input with 5 decimal places.
This will return col1 through col4 to the user or calling routine as an [N,4]-element array, e.g., col1 = output[*,0]. You could use a structure where each tag contains one of the colj arrays or you could return them through keywords.
Then you can pass these arrays to another function/program in the following way:
PRO my_algorithm_wrapper,file_name,RESULTS=results
;; Get data from files
columns = read_my_file(file_name)
;; Pass data to [algorithm] function
results = my_algorithm(columns[*,0],columns[*,1],columns[*,2],columns[*,3])
;; Return to user
RETURN
END
To call this from the command line (after making sure both routines are compiled), you would do something like the following:
IDL> my_algorithm_wrapper,file_name,RESULTS=results
IDL> HELP,results ;; see what the function my_algorithm.pro returned
The above code should work with IDL 6.2.
General Notes
Try to avoid using uppercase letters in IDL routine names as it can cause issues when IDL searches for the routine during a call or compilation statement.
You need to name the program/function in the line with the PRO/FUNCTION statement at the beginning of the file. The name must come immediately after the PRO/FUNCTION statement.
It is generally wise to use explicit formatting statements to avoid ambiguities/errors when reading data files.
You can pass any variable type (e.g., scalar integer, array, structure, object, etc.) to programs/functions so long as they are handled appropriately within the program/function.
I'm working on a problem where I have an array A of 100 elements.
All these 100 elements are changing with time.
So in my workspace, I only get the final values of all these elements after the entire time cycle has run.
I'm trying to save the values with time in a separate file (.txt or .mat) so that I can access that file in order to check how the variable varies with time.
I'm trying the following command:
save('file.mat','A','-append');
But this command overwrites the existing values in my file.
Kindly suggest me a way to save these values without overwriting them and also guide me how to access them in MATLAB.
You can also change the output filename to be unique for each iteration:
for iter=1:n
A = rand(10);
save(sprintf('file%d.mat',iter), 'A');
end
That way each iteration creates one file.
The reason that saving to a file (even using the -append) flag didn't work is because the variable A already exists in the file and will be over-written each time through the loop. You would need to create a new file or new variable name every time through the loop in order for this to not happen.
Saving the results in a file is probably not the best way to store the time-varying values of A. You would be better off using a cell array to store all intermediate values of A.
A_over_time = cell();
for k = 1:n
%// Get A somehow
A_over_time{k} = A;
end
Depending on what A is, you could also store the values of A in a numeric array or matrix.
%// Using an array
A_over_time = zeros(N, 1);
for k = 1:N
A_over_time(k) = A;
end
%// Using a matrix
A_over_time = zeros(N, numel(A));
for k = 1:N
A_over_time(k,:) = A;
end
So I would like to optimize my code such that I can look through an array such as
{'one','two','three'}
and create corresponding variables defined as tables or arrays
such as
one = table()
two = table()
three = table()
I am aware of the eval function however I would like to use this function in a loop s.t I allocate values to the new variable right after i create it
If I am understanding your question properly, given a cell array consisting only of strings, you wish to create variables in your workspace where each variable is declared as a string using the names from this cell array.
You could use eval, but I'm going to recommend something other than eval. eval should be avoided and instead of iterating those reasons, you can read Loren Shure's article on eval.
In any case, I would recommend you place these variables as dynamic fields inside a structure instead. Do something like this:
s = struct()
strs = {'one', 'two', 'three'};
for idx = 1 : numel(strs)
s.(strs{idx}) = table();
end
In this case, s would be a structure, and you can access the variable by the dot operator. In this case, you can access the corresponding variables by:
d = s.one; %// or
d2 = s.two; %// or
d3 = s.three;
If you want to place this into a function per se, you can place this into a function like so:
function [s] = createVariables(strs)
s = struct();
for idx = 1 : numel(strs)
s.(strs{idx}) = table();
end
This function will take in a cell array of strings, and outputs a structure that contains fields that correspond to the cell array of strings you put in. As such, you'd call it like so:
strs = {'one', 'two', 'three'};
s = createVariables(strs);
However, if you really really... I mean really... want to use eval, you can create your workspace variables like so:
strs = {'one', 'two', 'three'};
for idx = 1 : numel(strs)
eval([strs{idx} ' = table();']);
end
To place this into a function, do:
function [] = createVariables(strs)
for idx = 1 : numel(strs)
eval([strs{idx} ' = table();']);
end
However, be warned that if you run the function above, these variables will only be defined in the scope that the function was run in. You will not see these variables when the function exits. If you want to run a function so that the variables get defined in the workspace after you run the function, then eval is not the right solution for you. You should thus stick with the dynamic field approach that I talked about at the beginning of this post.
In any case, this will create one, two and three as workspace variables that are initialized to empty tables. However, I will argue with you that the first line of code is easier to read than the second piece of code, which is one of the main arguing points as to why eval should be avoided. If you stare at the second piece of code long enough, then you can certainly see what we're trying to achieve, but if you read the first piece of code, you can ascertain its purpose more clearly.