Cookbook
An Octave cookbook. Each entry should go in a separate section and have the following subsection: problem, solution, discussion and maybe a see also.
Programs, Libraries, and PackagesEdit
Recipes for developers of Octave programs and libraries. The type of stuff an Octave programmer should be aware when writing code for others.
Find Octave configurationEdit
Octave can be built with many configurations so programs may end up running in a machine without features they need. Developers should never expect an Octave installation to have all features. And programs should identify if the required features are available.
This is a list of possible tests to check for features:
## support for 64 bit indexing
sizemax () > intmax ("int32")
## built with support for java
usejava ("jvm")
## Image IO with support for tif files
any (cellfun (@(x) ismember ("tif", x), {imformats.ext}))
## Image IO with support for png files
any (cellfun (@(x) ismember ("png", x), {imformats.ext}))
Find if a package is installedEdit
ProblemEdit
You have a program that uses different functions or behaves different depending on the availability of specific packages.
SolutionEdit
Use pkg ("list", pkg-name)
like so:
if (! isempty (pkg ("list", "foo")))
## use functions from package foo, the preferred way
elseif (! isempty (pkg ("list", "bar")))
## use functions from package bar, not so optimal
else
## default case
endif
DiscussionEdit
It's not recommended to use this if the only purpose is to then fail in the absence of the package. In such case, simply try to load the package and Octave will already give a error message that is informative enough.
There is only purpose to check this, if there is something different to
do if a package is missing. The same is true for catching an error from
pkg load
. If you only catch an error to then throw it again
then you might as well not catch it in the first place.
## This contraption doesn't add anything. If 'pkg load' fails, it
## will already give an error message to the user.
try
pkg load foo;
catch
error ("failed to load foo: %s", lasterr ());
end_try_catch
## Again, doesn't add anything. The failure of 'pkg load' is enough
if (isempty (pkg ("list", "foo")))
error ("program: package foo is not installed");
endif
Beware that an installed package is not always a guarantee that a function will be available. Some packages may disable functions at build time, or specific functions may have specific runtime requirements.
List all function in OctaveEdit
Use the following script (filename list_func.m
)
## List of all builtin (C++) functions and m-file functions
funcs = vertcat (__builtins__ (), __list_functions__ ());
## Write list to file
fid = fopen ("all_funcs.tmp", "w");
if (fid == -1)
error ("Unable to open temporary file all_funcs.tmp. Aborting...\n");
endif
fprintf (fid, "%s\n", funcs{:});
fclose (fid);
And execute with
run-octave -f list_func.m
StructuresEdit
Retrieve a field value from all entries in a struct arrayEdit
ProblemEdit
You have a struct array with multiple fields, and you want to access the value from a specific field from all elements. For example, you want to return the age from all patients in the following case:
samples = struct ("patient", {"Bob", "Kevin", "Bob" , "Andrew"},
"age", { 45 , 52 , 45 , 23 },
"protein", {"H2B", "CDK2" , "CDK2", "Tip60" },
"tube" , { 3 , 5 , 2 , 18 }
);
SolutionEdit
Indexing the struct returns a comma separated list so use them to create a matrix.
[samples(:).age]
This however does not keep the original structure of the data, instead returning all values in a single column. To fix this, use reshape()
.
reshape ([samples(:).age], size (samples))
DiscussionEdit
Returning all values in a comma separated lists allows you to make anything out of them. If numbers are expected, create a matrix by enclosing them in square brackets. But if strings are to be expected, a cell array can also be easily generated with curly brackets
{samples(:).patient}
You are also not limited to return all elements, you may use logical indexing from other fields to get values from the others:
[samples([samples(:).age] > 34).tube] ## return tube numbers from all samples from patients older than 34
[samples(strcmp({samples(:).protein}, "CDK2")).tube] ## return all tube numbers for protein CDK2
Array manipulationEdit
Select a slice from an n-D arrayEdit
ProblemEdit
For an array A
with arbitrary number of dimensions, select, for example, the first column.
This would be A(:, 1)
if A
was 2-D, A(:, 1, :)
if A
was 3-D, and so on.
SolutionEdit
One possibility is to use subsref
with the input idx
created dynamically with repelems
to have the right number of dimensions.
This can be written as a function:
function [B]= array_slice (A,k,d)
#return the k-th slice (row, column...) of A, with d specifying the dimension to slice on
idx.type = "()";
idx.subs = repelems ({':'}, [1;ndims(A)]);
idx.subs(d) = k;
B = subsref (A,idx);
endfunction
#test cases
%!shared A
%! A=rand(2, 3);
%!assert (array_slice (A,1,2), A(:, 1))
%! A=rand(2, 3, 4);
%!assert (array_slice (A,2,1), A(2, :, :))
%! A=rand(2, 3, 4, 5);
%!assert (array_slice (A,1,2), A(:, 1, :, :))
%! A=rand(2, 3, 4, 5, 6);
%!assert (array_slice (A,2,3), A(:, :, 2, :, :))
To remove the singleton dimension d
from the result B
, use
B = reshape(B, [size(B)([1:d-1 d+1:end])]);
Input/outputEdit
Display matched elements from different arraysEdit
ProblemEdit
You have two, or more, arrays with paired elements and want to print out a string about them. For example:
keys = {"human", "mouse", "chicken"};
values = [ 64 72 70 ];
and you want to display:
Calculated human genome GC content is 64% Calculated mouse genome GC content is 72% Calculated chicken genome GC content is 70%
SolutionEdit
Make a two rows cell array, with each paired data in a column and supply a cs-list to printf
values = num2cell (values);
new = {keys{:}; values{:}};
printf ("Calculated %s genome GC content is %i%%\n", new{:})
or in a single line:
printf ("Calculated %s genome GC content is %i%%\n", {keys{:}; num2cell(values){:}}{:})
DiscussionEdit
printf
and family do not accept cell arrays as values.
However, they keep repeating the template given as long as it has enough arguments to keep going.
As such, the trick is on supplying a cs-list of elements which can be done by using a cell array and index it with {}
.
Since values are stored in column-major order, paired values need to be on the same column. A new row of data can then be added later with
new(end+1,:) = {"Andrew", "Bob", "Kevin"};
Note that normal brackets are now being used for indexing.
Swap valuesEdit
If you want to exchange the value between two variables without creating a dummy one, you can simply do:
[b,a] = deal (a,b);
Collect all output arguments of a functionEdit
If you have a function that returns several values, e.g.
function [a b c]= myfunc ()
[a,b,c] = deal (1,2,3);
endfunction
and you want to collect them all into a single cell (similarly to Python's zip() function) you can do:
outargs = nthargout (1:3, @myfunc)
Create a text table with fprintfEdit
Imagine that you want to create a text table with fprintf
with 2 columns of 15 characters width and both right justified.
How to do this thing?
That's easy:
If the variable Text is a cell array of strings (of length < 15) with two columns and a certain number of rows, simply type for the k-th row of Text
fprintf('%15.15s | %15.15s\n', Text{k,1}, Text{k,2});
The syntax %<n>.<m>s
allocates n
places to write chars and display the m
first characters of the string to display.
Example:
Text = {"Hello", "World"};
fprintf('%15.15s | %15.15s\n', Text{1,1}, Text{1,2})
Hello | World
Load comma separated values (*.csv) filesEdit
- Using
textread
gets you a one column cell array. The original size can be restored by using thereshape
function.A = textread("file.csv", "%d", "delimiter", ","); B = textread("file.csv", "%s", "delimiter", ","); inds = isnan(A); B(! inds) = num2cell (A(! inds))
- Another option is to use the function
csvread
. However, this function can't handle non-numerical data. - The probably best option is to use the function
csv2cell
from the IO package. This function can read mixed-type (numerical and text) .csv files, allows to specify other field separators than a comma and other text protection characters (default:"
double quote) and can skip header lines. If you have the IO package installed and loaded, typehelp csv2cell
at the Octave prompt for more info.
Load XML filesEdit
Reading XML in octave can be achieved using the java library Apache Xerces.
It seems that the Matlab's xmlread
is just a thin wrapper around the Apache Xerces library.
One should note however, that Java functions have the working directory set to the working directory when octave starts and the working directory is not modified by a cd
in octave.
Matlab has the same behavior, as Java does not provide a way to change the current working directory (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688).
To avoid any issues, it is thus better to use the absolute path to the XML file.
You need the jar files xercesImpl.jar and xml-apis.jar from e.g. https://xerces.apache.org/mirrors.cgi#binary (check for the latest version).
Use javaaddpath
to include these files:
javaaddpath ("/path/to/xerces-2_11_0/xercesImpl.jar");
javaaddpath ("/path/to/xerces-2_11_0/xml-apis.jar");
Example:
filename = "sample.xml";
## These three lines are equivalent to xDoc = xmlread(filename) in Matlab
parser = javaObject("org.apache.xerces.parsers.DOMParser");
parser.parse(filename);
xDoc = parser.getDocument();
elem = xDoc.getElementsByTagName("data").item(0); ## get first data element
data = elem.getFirstChild.getTextContent(); ## get text from child
att = elem.getAttribute("att"); ## get attribute named att
<root>
<data att="1">hello</data>
</root>
Using variable strings in commandsEdit
For example, to plot data using a string variable as a legend:
- Static string as legend (simplest):
x = linspace (-1, 3, 100); y = sin (x); legend = "-1;My data;"; plot (x, y, legend);
- Variable string as legend (moderate):
x = linspace (-1, 3, 100); y = sin (x); dataName = "My data"; plot (x, y, sprintf("-1;%s;", dataName));
- Variable string as legend using
eval
(not as neat):legend = "My data"; plot_command = ["plot (x, y, '-1;", legend, ";')"]; eval (plot_command);
These same tricks are useful for reading and writing data files with unique names, etc.
CombinatoricsEdit
Combinations with string charactersEdit
ProblemEdit
You want to get all combinations of different letters but nchoosek
only accepts numeric input.
SolutionEdit
Convert your string to numbers and then back to characters.
string = "Hello";
n = 4;
char (nchoosek (uint8 (string), n))
DiscussionEdit
A string in Octave is just a character matrix and can easily be converted to numeric form back and forth.
Each character has an associated number (the asci
function of the miscellaneous package displays a nicely formatted conversion table).
Permutations with repetitionEdit
ProblemEdit
You want to generate all possible permutations of a vector with repetition.
SolutionEdit
Use ndgrid
:
[x, y, z] = ndgrid ([1, 2, 3, 4, 5]);
[x(:), y(:), z(:)]
DiscussionEdit
It is possible to expand the code above and make it work for any length of permutations.
cart = nthargout ([1:n], @ndgrid, vector);
combs = cell2mat (cellfun (@(c) c(:), cart, "UniformOutput", false));
MathematicsEdit
Test if a number is an integerEdit
The simplest method is probably fix
:
fix (x) == x
Find if a number is even/oddEdit
ProblemEdit
You have a number, or an array or matrix of them, and want to know if any of them is an odd or even number, i.e., their parity.
SolutionEdit
Check the remainder of a division by two. If the remainder is zero, the number is even.
mod (value, 2) ## 1 if odd, zero if even
Since mod
accepts a matrix, the following can be done:
any (mod (values, 2)) ## true if at least one number in values is even
all (mod (values, 2)) ## true if all numbers in values are odd
any (!logical (mod (values, 2))) ## true if at least one number in values is even
all (!logical (mod (values, 2))) ## true if all numbers in values are even
DiscussionEdit
Since we are checking for the remainder of a division, the first choice would be to use rem
.
However, in the case of negative numbers mod
will still return a positive number making it easier for comparisons.
Another alternative is to use bitand (X, 1)
or bitget (X, 1)
but those are a bit slower.
Note that this solution applies to integers only.
Non-integers such as 0.5
or 4.201
are neither even nor odd.
If the source of the numbers are unknown, such as user input, some sort of checking should be applied for NaN
, Inf
, or non-integer values.
Parametrized FunctionsEdit
ProblemEdit
One sometimes needs to define a family of functions depending on a set of parameters, e.g., where denote a the variables on which the function operates and are the parameters used to chose one specific element of the family of functions.
For example, let's say we need to compute the time evolution of the elongation of a spring for different values of the spring constant
SolutionEdit
We could solve the problem with the following code to solve the spring equation for different values of the spring constant:
t = linspace (0, 10, 100);
function sprime = spring (s, t, k)
x = s(1);
v = s(2);
sprime(1) = v;
sprime(2) = -k * x;
endfunction
k = 1;
x1 = lsode (@(x, t) spring (x, t, k), [1;0], t)(:, 1);
k = 2;
x2 = lsode (@(x, t) spring (x, t, k), [1;0], t)(:, 2);
plot (t, x1, t, x2)
legend ('x1', 'x2')
DiscussionEdit
In the above example, the function "sprime" represents a family of functions of the variables parametrized by the parameter .
@(x, t) sprime (x, t, k)
is a function of only where the parameter is "frozen" to the value it has at the moment in the current scope.
Distance between pointsEdit
ProblemEdit
Given a set of points in space we want to calculate the distance between all of them.
Each point is described by its components .
Assume that the points are saved in a matrix P
with m
rows (one for each point) and n
columns, one for each component.
SolutionEdit
One way of proceeding is to use the broadcast properties of operators in GNU Octave. The square distance between the points can be calculated with the code:
[m, n] = size (P);
Dsq = zeros (m);
for i = 1:n
Dsq += (P(:,i) - P(:,i)').^2;
endfor
This matrix is symmetric with zero diagonal.
Similarly the vectors pointing from one point to the another is
R = zeros (m, m, n);
for i = 1:n
R(:,:,i) = P(:,i) - P(:,i)';
endfor
The relation between Dsq
and R
is
Dsq = sumsq (R, 3);
DiscussionEdit
The calculation can be implemented using functions like cellfun
and avoid the loop over components of the points.
However in most cases we will have more points than components and the improvement, if any, will be minimal.
Another observation is that the matrix Dsq
is symmetric and we could store only the lower or upper triangular part.
To use this optimization in a practical way check the help of the functions vech
and unvech
(this one is in the Forge package general).
Two functions that haven't seen the light yet are sub2ind_tril
and ind2sub_tril
(currently private functions in the Forge package mechanics) that are useful to index the elements of a vector constructed with the function vech
.
Each page (the third index) of the multidimensional array R
is an anti-symmetric matrix and we could also save some memory by keeping only one of the triangular submatrices.
Check the Geometry package for many more distance functions (points, lines, polygons, etc.).
Find Algebraic Variables from ODE State VariablesEdit
ProblemEdit
When using lsode or other ODE solver to find the state variables x as a function of time t in a set of ODE's, there seems to be no simple way to return and tabulate algebraic variables y that are a function of the state variables x and time t, other than the derivatives.
Consider a modified version of the spring function defined above in the section on parametric functions:
function sprime=dspring (s, t)
k=1;
tau=1;
x = s(1);
v = s(2);
y=x+v*tau;
sprime(1) = v;
sprime(2) = -k * y;
endfunction
What would be a way to way to use this function and lsode to tabulate not just the elements of x and sprime, but also (say) y as a function of time?
SolutionEdit
Modify the function sprime to return y as a second argument:
function [sprime,y]=dspring (s, t)
k=1;
tau=1;
x = s(1);
v = s(2);
y=x+v*tau;
sprime(1) = v;
sprime(2) = -k * y;
endfunction
lsode will happily ignore the second return argument y, but after x has been tabulated, y can be tabulated using the second return argument of the spring function in a for loop, e.g.
t = linspace (0, 10, 100);
x = lsode ('dspring',[1;0], t);
y=zeros(length(t),1);
for i=1:length(t)
[xtmp,y(i,1)]=dspring(x(i,:),t(i));
end
DiscussionEdit
This is a simple example with one algebraic variable. Others can be added by extending the second dimension of the y array.