Installing Julia on Ubuntu

By: DSB

Re-posted from: https://medium.com/coffee-in-a-klein-bottle/install-julia-1-5-on-ubuntu-bb8be4b2571d?source=rss-8bd6ec95ab58------2

A quick tutorial on how to install Julia on Ubuntu and add the kernel to Jupyter

Installing Julia on Ubuntu is very straightforward. As anything on Linux, there are several ways to do the installation.

1. Using Apt-Get

This is the first way, and it is the easiest. Just open the terminal and run

sudo apt-get install julia

The problem with this method is that you don’t get the latest stable version for Julia. In the time I’m writing this article, the Ubuntu repository contains the version 1.0.4 for Julia, while the current stable version is 1.5.2.

So this is not the method I recommend!

2. From the Website (recommended)

First, go to the Julia website download page. There, several ways to run Julia are shown. Go to the “Current stable release” table, and click on the link shown in the image below.

Donwload Julia by clicking on the “64-bit” inside the red square

Then, the Julia Language will be donwloaded (most likely to your Download directory). Next, go on your terminal and unzip the downloaded file.

tar -xvzf julia-1.5.2-linux-x86_64.tar.gz

You now have a folder with all the Julia files. No installation is required. Now, we move this folder to “/opt” (this is not strictly necessary, you can use any other location in your machine).

sudo mv julia-1.5.2/ /opt/

Finally, create a symbolic link to the Julia binary file. This will allow you to run the command “julia” in your terminal and start the Julia REPL.

sudo ln -s /opt/julia-1.5.2/bin/julia /usr/local/bin/julia

Done! Now Julia is already installed and working in your machine. Let’s now add it to Jupyter.

3. Adding Julia kernel to Jupyter

We assume that Jupyter is already installed in your machine. To add the Julia Kernel to it is quite easy. Just open your terminal and run “julia”. This will open the REPL, as shown in the image below.

Inside the REPL, we then run the following commands:

using Pkg
Pkg.add(“IJulia”)

The “Pkg” package is used in Julia to install different packages on Julia. By installing “IJulia”, it automatically adds the Julia kernel to Jupyter.

And that’s it. Julia should be working with your Jupyter Notebook and/or JupyterLab.


Installing Julia on Ubuntu was originally published in Coffee in a Klein Bottle on Medium, where people are continuing the conversation by highlighting and responding to this story.

Julia Base is DataFrames.jl best friend

By: Blog by Bogumił Kamiński

Re-posted from: https://bkamins.github.io/julialang/2020/10/02/performance.html

Introduction

Recently I received a very interesting question about the performance
of operations on GroupedDataFrame in DataFrames.jl.

In this post I want to share some comments related to the performance of DataFrames.jl.

The TLDR summary is:
if you want top performance fall back to low-level Julia
code (and DataFrames.jl exposes API that allows you to follow that path).

In this post I am using Julia 1.5.2 and DataFrames.jl 0.21.7.

The question and a direct answer

Assume you have a large data frame df that has two columns: :id and :v
(in practice also many more columns that are irrelevant so I skip them).
For each unique value of :id you want to keep only one row that corresponds
to the smallest value of :v in the group defined by column :id.

We start with generating the example DataFrame and grouping it by :id column:

julia> using DataFrames, Random

julia> Random.seed!(1234);

julia> df = DataFrame(id=rand(1:10^6, 10^7), v=rand(10^7))
10000000×2 DataFrame
│ Row      │ id     │ v        │
│          │ Int64  │ Float64  │
├──────────┼────────┼──────────┤
│ 1        │ 99353  │ 0.954945 │
│ 2        │ 371388 │ 0.103541 │
⋮
│ 9999998  │ 71163  │ 0.37311  │
│ 9999999  │ 529940 │ 0.856262 │
│ 10000000 │ 845766 │ 0.401057 │

julia> gdf = groupby(df, :id);

julia> @time gdf = groupby(df, :id)
  1.326534 seconds (30 allocations: 280.590 MiB)
GroupedDataFrame with 999963 groups based on key: id
First Group (13 rows): id = 99353
│ Row │ id    │ v         │
│     │ Int64 │ Float64   │
├─────┼───────┼───────────┤
│ 1   │ 99353 │ 0.954945  │
│ 2   │ 99353 │ 0.584677  │
⋮
│ 11  │ 99353 │ 0.838052  │
│ 12  │ 99353 │ 0.0773429 │
│ 13  │ 99353 │ 0.774979  │
⋮
Last Group (1 row): id = 502811
│ Row │ id     │ v        │
│     │ Int64  │ Float64  │
├─────┼────────┼──────────┤
│ 1   │ 502811 │ 0.953105 │

(note that I run here and below all commands twice to exclude compilation time
and allocations from what is reported by @time macro)

A direct approach to solve this problem would be the following (here I store the
result in res1 variable to compare it to other solutions at the end of this post):

julia> res1 = combine(sdf -> first(sort(sdf)), gdf);

julia> @time combine(sdf -> first(sort(sdf)), gdf)
 13.587720 seconds (296.78 M allocations: 12.672 GiB, 9.73% gc time)
999963×2 DataFrame
│ Row    │ id     │ v         │
│        │ Int64  │ Float64   │
├────────┼────────┼───────────┤
│ 1      │ 99353  │ 0.0436469 │
│ 2      │ 371388 │ 0.0203823 │
⋮
│ 999961 │ 167495 │ 0.228266  │
│ 999962 │ 527369 │ 0.0525908 │
│ 999963 │ 502811 │ 0.953105  │

We see that the operation is quite slow and massively allocates. The problem is
that sort(sdf) allocates a fresh small DataFrame for each group. This is
inefficient.

Let us think then if we can improve the performance of this code.

Pre-sorting

The first option to consider is pre-sorting of df like this:

julia> res2 = combine(first, groupby(sort(df, :v), :id));

julia> @time combine(first, groupby(sort(df, :v), :id))
  7.166366 seconds (14.03 M allocations: 1.811 GiB, 2.85% gc time)
999963×2 DataFrame
│ Row    │ id     │ v          │
│        │ Int64  │ Float64    │
├────────┼────────┼────────────┤
│ 1      │ 533731 │ 1.27094e-7 │
│ 2      │ 709049 │ 1.29805e-7 │
⋮
│ 999961 │ 803454 │ 0.991274   │
│ 999962 │ 107403 │ 0.99218    │
│ 999963 │ 925877 │ 0.994074   │

We already see much improvement. However, this solution has a problem that
it still requires sorting, which in the chain sort, groupby, combine
is the slowest step.

Using argmin

Actually we see that we do not need to sort our data to get the result. It
is enough to find the minimum row with respect to column :v. Here is how
one can do it:

julia> res3 = combine(sdf -> sdf[argmin(sdf.v), :], gdf);

julia> @time combine(sdf -> sdf[argmin(sdf.v), :], gdf)
  1.581478 seconds (15.48 M allocations: 593.183 MiB, 4.29% gc time)
999963×2 DataFrame
│ Row    │ id     │ v         │
│        │ Int64  │ Float64   │
├────────┼────────┼───────────┤
│ 1      │ 99353  │ 0.0436469 │
│ 2      │ 371388 │ 0.0203823 │
⋮
│ 999961 │ 167495 │ 0.228266  │
│ 999962 │ 527369 │ 0.0525908 │
│ 999963 │ 502811 │ 0.953105  │

Now we are much faster. We can do better if we start using a low-level API
of DataFrames.jl and use Julia Base functionality.

Going for Julia Base

I will present two solutions here that do the job faster than the last
example of combine. The first one uses the fact that we can iterate
GroupedDataFrame and parentindices function returns us a tuple of indices
into the parent of the passed object. Here is the approach that can be used:

julia> res4 = df[[parentindices(sdf)[1][argmin(sdf.v)] for sdf in gdf], :];

julia> @time df[[parentindices(sdf)[1][argmin(sdf.v)] for sdf in gdf], :]
  0.990385 seconds (5.19 M allocations: 325.875 MiB, 4.73% gc time)
999963×2 DataFrame
│ Row    │ id     │ v         │
│        │ Int64  │ Float64   │
├────────┼────────┼───────────┤
│ 1      │ 99353  │ 0.0436469 │
│ 2      │ 371388 │ 0.0203823 │
⋮
│ 999961 │ 167495 │ 0.228266  │
│ 999962 │ 527369 │ 0.0525908 │
│ 999963 │ 502811 │ 0.953105  │

And we see that we are somewhat faster. We can go much faster by using the
groupindices function that gives us a group number for each row of the df
data frame. The cost is that the solution is a bit more verbose:

julia> function getfirst(gdf, v)
           loc = zeros(Int, length(gdf))
           best = Vector{eltype(v)}(undef, length(gdf))
           for (i, (ind, vi)) in enumerate(zip(groupindices(gdf), v))
               if !ismissing(ind) && (loc[ind] == 0 || vi < best[ind])
                   best[ind] = vi
                   loc[ind] = i
               end
           end
           return parent(gdf)[loc, :]
       end
getfirst (generic function with 1 method)

julia> res5 = getfirst(gdf, df.v);

julia> @time getfirst(gdf, df.v)
  0.359210 seconds (20 allocations: 116.349 MiB)
999963×2 DataFrame
│ Row    │ id     │ v         │
│        │ Int64  │ Float64   │
├────────┼────────┼───────────┤
│ 1      │ 99353  │ 0.0436469 │
│ 2      │ 371388 │ 0.0203823 │
⋮
│ 999961 │ 167495 │ 0.228266  │
│ 999962 │ 527369 │ 0.0525908 │
│ 999963 │ 502811 │ 0.953105  │

We could go even faster (at least twice in my tests), but let me stop here and
leave the potential improvements as an exercise.

Additionally, it is worth to mention that the vector groupindices(gdf) has a
value equal to missing if some row from df is not mapped to any group in
gdf (note that GroupedDataFrame can be indexed-into).

Conclusions

Before we conclude let us check if the five solutions give us the same
results:

julia> length(unique(sort.([res1, res2, res3, res4, res5])))
1

Indeed they do. I had to use sort here, as res2 has a different row order
than the rest of data frames holding the results.

What are the take aways from these examples:

  • if you think about avoiding allocations and doing unnecessary work (sorting
    in our case) you can get a reasonably fast result using high-level API of
    DataFrames.jl;
  • if you want to go really fast there is always an option to fall-back to
    custom code written in Julia Base and DataFrames.jl has a low-level API
    that allows you to efficiently do this.

The reason why high-level API is slower is that it handles dozens of possible
use cases under one function and going low-level allows you do do only the thing
that needs to be done in your use case.

In general, it is worth to highlight here that, as opposed to e.g. R, DataFrame
in Julia is just one of the many data structures you can use. The high-level API
DataFrames.jl provides will be fast enough in most of the cases (at the benefit
of being very flexible). However, if things are too slow just go low-level,
which is a natural thing to do in Julia.

Bayesian Inference on State Space Models – Part 1

By: Posts | Welcome!

Re-posted from: https://paschermayr.github.io/post/statespacemodels-3-inference-on-state-space-models-part-1/

Inference on State Space Models – an Overview

Hi there!

In my previous posts, I introduced two discrete state space model (SSM) variants: the

hidden Markov model and

hidden semi-Markov model. However, in all code examples,
model parameter were already given – what happens if we need to estimate them? This post is the first of a series of four blog entries, in which I will explain a general framework to perform parameter
inference on SSMs and implement a basic example for demonstration. Ideally, I would make all three posts as applied as my previous articles, but I have to use some theory in the
beginning to make things easier going forward.

While SSMs are very flexible and can describe data with a complex structure, parameter estimation can be very challenging.
Analytical forms of the corresponding likelihood functions are only available in special cases
and, thus, standard parameter optimization routines might be unfeasible. Consequently, the major challenge in solving SSMs is the generally intractable
likelihood function $p_{\theta}(e_{1:t}) = \int p_{\theta}(e_{1:t}, s_{1:t}) d s_{1:t}$, which integrates over the (unknown) latent state trajectory. As previously mentioned,
$E_t$ is the observed data and $S_t$ the corresponding latent state.

So what can we do?

If both $E_t$ and $S_t$ follow a Normal distribution, one may analytically estimate the corresponding model parameter via the so called Kalman equations,
but given these very specific assumptions, we will not concentrate on this special case going forward.
If $S_{1:T}$ is discrete, then the most common point estimation technique is the EM-Algorithm.
In this case, one can use common filtering techniques, explained in (Rabiner, 1989), to iteratively calculate state probabilities and update model parameter.
Once the EM-Algorithm has reached some convergence criterion, one may estimate the most likely state trajectory given the estimated parameter. I will not go into
much detail here because this procedure has been explained on many occasions and will instead refer to the literature above.
However, both approaches do not work in many cases. The former technique only works with very specific model assumptions.
The latter technique is not available in case $S_{1:T}$ is continuous. Moreover, even if the state sequence is
discrete, summing out the state space might be computationally very challenging if the latent state structure is complex. In addition, one may not be able to
write down the analytical forms of the updating equations for more complex state space models.

Another popular approach is to use Gibbs sampling, where one uses the previously mentioned filtering techniques to obtain a sample from the
whole state trajectory $S_{1:T}$, and then sample model parameter conditional on this path.
This technique suffers from the usually high autocorrelation within the model parameter vector and the state sequence.
Moreover, ideally one would like to perform estimation jointly for the model parameter $\theta$ and the latent state space $S_{1:T}$, as both have a high interdependence as well. This is not feasible for either method that I mentioned so far.
For a more in-depth discussion, an excellent comparison of point estimation and Bayesian techniques is given by (Ryden, 2008).

A general framework to perform inference on state space models

Given the statements above, I will now focus on a general approach to perform joint estimation on the model parameter and state trajectory of state space models,
independent of the variate form of the latent space and the distributional assumptions on the observed variables. This is, to the best of my
knowledge, only possible in a Bayesian setting, where we target the full joint posterior $P( s_{1:T}, \theta \mid e_{1:T})$ by iterating over the following steps:

    1. propose $\theta^{\star} \sim f(\theta^{\star} \mid \theta)$
    1. propose $S^\star_{1:T} \sim P(s^{\star}_{1:T} \mid \theta^{\star}, e_{1:T})$.
    1. and then accept this pair $(\theta^{\star}, s^{\star}_{1:T})$ with acceptance probability

$$
\begin{equation}
\begin{split}
A_{PMCMC} &= \frac{ P( s^{\star}_{1:T} \mid \theta^{\star}) }{ P(s_{1:T} \mid \theta ) }
\frac{ P(e_{1:T} \mid s^{\star}_{1:T}, \theta^{\star}) }{ P(e_{1:T} \mid s_{1:T}, \theta ) }
\frac{ P(s_{1:T} \mid e_{1:T}, \theta) }{ P(s^{\star}_{1:T} \mid e_{1:T}, \theta^{\star}) }
\frac{P(\theta^{\star})}{P(\theta)} \frac{q(\theta \mid \theta^{\star})}{q(\theta^{\star} \mid \theta)} \\
&= \frac{P(e_{1:T} \mid \theta^{\star})}{P(e_{1:T} \mid \theta)} \frac{P(\theta^{\star})}{P(\theta)} \frac{q(\theta \mid \theta^{\star})}{q(\theta^{\star} \mid \theta)}. \
\end{split}
\end{equation}
$$

Okay, but what does that even mean? In the first step, we propose a new parameter vector $\theta^{\star}$ from a MCMC proposal distribution. I will not go over the basics
of MCMC, as this has been explained in many other articles, but we will implement an example in part 3 of this tutorial. Given this $\theta^{\star}$, we sample a new trajectory
$s^\star_{1:T}$ and jointly accept this pair with the acceptance ratio from point 3. This framework allows to jointly sample $\theta$ and $S_{1:T}$, which was our goal in the first place.
However, the in general intractable likelihood term is still contained in the acceptance probability in step 3,
which is simplified by using the basic marginal likelihood identity (BMI) from (Chib, 1995).
Consequently, the difficulty to obtain a sample from the posterior remains the same: we need to evaluate the (marginal) likelihood of the model.

The Particle MCMC idea

An alternative is to replace this (marginal) likelihood with an unbiased estimate $\hat{ \mathcal{L} }$$_\theta(e_{1:T})$. In this setting, (Andrieu and Roberts, 2010) have shown the puzzling result that one
can do so and still target the exact posterior distribution of interest, thereby opening a completely new research area now called ‘exact approximate MCMC’.
The algorithm that is used in this setting targets the full posterior and is known as Particle MCMC (PMCMC).
Here, a particle filter (PF) is used to obtain a sample for $S_{1:T}$. A by-product of this procedure is that we also obtain an unbiased estimate for the likelihood.
If you are more interested in PFs, have a look at the paper from (Doucet, A. and Johansen, 2011).
For anyone wondering: as $p(s_{1:T} \mid e_{1:T}, \theta )$ is replaced with an approximation $\hat{p}(s_{1:T} \mid e_{1:T}, \theta )$,
the approximation does not admit $p(s_{1:T}, \theta \mid e_{1:T})$ as invariant density, but
this is corrected in the PMCMC algorithm via step 3.

Going forward

The PMCMC machinery provides a very powerful framework and cures many of the difficulties in the estimation paradigms mentioned at the
beginning of this series. In reality, however, much of the performance depends on whether you find a good proposal
distribution $ f( . \mid \theta)$ and $P(s^\star_{1:T} \mid \theta^{\star}, e_{1:T})$.
Moreover, we have yet not mentioned how this machinery scales in the dimension of $\theta$ and $S_{1:T}$.
These are all questions that I pursue in my own PhD studies, and we may venture through them once we know the very basics.

In the next blog post, we will implement a particle filter to do inference on the latent state trajectory. Going forward, we then talk about doing MCMC in this case,
and implement a basic framework to sample from an unconstrained parameter space. Last but not least, we will combine all of this and apply a PMCMC
algorithm on a standard HMM to obtain parameter estimates.

References

Andrieu, C. and Roberts, G. O. (2010). The pseudo-marginal approach for efficient monte carlo computations.
Ann. Statist., 37(2):697-725.

Chib, S. (1995). Marginal likelihood from the gibbs output. Journal of the American Statistical Association,
90(432):1313-1321.

Doucet, A. and Johansen, A. (2011). A tutorial on particle filtering and smoothing: Fifteen years later.

Rabiner, L. R. (1989). A tutorial on hidden markov models and selected applications in speech recognition.
Proceedings of the IEEE, 77(2):257-286.

Ryden, T. (2008). Em versus markov chain monte carlo for estimation of hidden markov models: A computational
perspective. Bayesian Analysis, 3(4):659-688.