Skip to content

Miscellaneous

Good advice on miscellaneous topics:

Artifacts (1.6)

A useful guide: Artifacts for Dummies

Dates (1.6)

Working with time durations seems to require converting the durations to millisecond integers first:

d1 = DateTime(2021,9,10,9); d2 = d1 + Minute(3) + Second(5);
dSeconds = Dates.value(d2-d1) / 1000;

Going back to time units requires rounding because Minute(dSeconds) throws an InexactError.

dMinutes = Minute(round(dSeconds / 60));

There is no formatted display of durations, only of dates:

Dates.format(d1, "HH:MM");

Enum (1.6)

@enum Fruit apple=1 orange=2 kiwi=3

The type is Fruit (concrete). So dispatch on the instances does not work.

EnumX.jl packages enums in a module. Avoids namespace conflicts.

Alternatives:

  • Define an abstract type with concrete subtypes Apple <: AbstractFruit.
  • Make parametric types Fruit{:apple}. Static dispatch works.

Benefits of enum:

  • Array{Fruit} is more efficient than Array{AbstractFruit}. But can use Union{Apple, Orange, Kiwi}.

Benefits of types:

  • Static dispatch.
  • Can be extended.

File System

Base.Filesystem.mtime returns the modified time as a Float. Convert to DateTime using Dates.unix2datetime.

Useful packages:

Formatting Numbers

  • Formatting.jl is probably the go-to package.
  • PrettyNumbers.jl is a smart number formatting package. It automatically prints large numbers as 1.234 x 10^3.

Hashing

For new DataTypes, it is necessary to defined hash, ==, and isequal. hash is needed for containers such as Dicts (it is used for haskey). The way this is done can be based on AutoHashEquals.jl:

mutable struct Foo
    a::Int
    b
end
Base.hash(a::Foo, h::UInt) = hash(a.b, hash(a.a, hash(:Foo, h)))

Interpolation (1.6)

Interpolations.jl

An example from QuantEcon.

One limitation: multi-dimensional interpolation only works with unform grids. The inputs must be ranges.

The default for extrapolation is to throw an error.

Example:

xV = 1:10; yV = 5:10;
zM = xV .+ yV' .+ xV .* yV';
itp = CubicInterpolation((xV, yV), zM);
itp(1.5, 5.9)

The same (?) interpolation can be constructed the long way round:

itp2 = interpolate(zM, BSpline(Cubic(Line(OnGrid()))));
sitp = scale(itp2, xV, yV);

One difference is that bounds works on sitp but not on itp.

Latex Output (1.10)

SummaryTables.jl looks very powerful for generating fully formatted tables from DataFrames.

Latexify.jl renders various expressions as Latex equations. This is useful for automatically making expressions for functional forms. Also generates Latex tables from DataFrames and Arrays.

PrettyTables.jl can be used to produce Latex tables (and human readable text tables with the same content).

  • set wrap_table = false to avoid the \begin{tabular} block for a float.

Logging (1.6)

@debug can be used to generate self-test code that is only run while developing the code.

Example:

# test1.jl
a = 1;
b = [1,2,3];
@debug begin
   bSum = sum(b);
   """
   Debug message
   $bsum
   """
end
@debug "Debug with args:"  a, b

To run this with the @debug statements enabled, issue (in the shell):

export JULIA_DEBUG=all
julia "test1.jl"

But this generates lots of debug messages that sit in Base. To avoid this, export JULIA_DEBUG=MyPkg.

Enabling logging levels temporarily:

lg = ConsoleLogger(stderr, Logging.Debug);
with_logger(lg) do
   foo();
end

Or:

lg = ConsoleLogger(stderr, Logging.Debug);
old_logger = global_logger(lg);
foo();
global_logger(old_logger);

Random numbers (1.5)

Generating reproducible random numbers across Julia versions:

  • This can be done with StableRNGs.jl. This also seems to generate the same random numbers across operating systems (in my case MacOS and Linux).

Drawing integers with given probabilities:

RegEx

ReadableRegex.jl helps to construct RegEx.

Search and Find

searchsorted is useful to find elements (exact matches only) in an Array.

Terminal output

Term.jl can do some neat things with formatted terminal output.

Writing output to stdout and a file:

Traits

Traits are based on the idea that functions can be dispatched statically depending on Types. The canonical example:

struct Flies end
struct DoesNotFly end

flies(x) = DoesNotFly

struct Duck end
flies(::Duck) = Flies

f(x) = f(flies(x), x)
f(::Type{Flies}, x) = "$x flies"
f(::Type{DoesNotFly}, x) = "$x walks"

d = Duck()

f(d)  # Output: "Duck() flies"
f(7)  # Output: "7 walks"

An interesting implementation is WhereTraits.jl which permits, for example, dispatch on functions that return Bool.