Category Archives: Julia

Life expectancy and transition in cause of death patterns

By: Karl Pettersson

Re-posted from: https://static-dust.klpn.se/posts/2022-03-27-transition.html

Life expectancy and transition in cause of death patterns

Posted on 2022-03-27

by Karl Pettersson.

Tags: epidemiology, julia

This week, the Swedish statistical agency has published life tables for
Sweden 2021 (Statistics Sweden 2022). With the first waves of the COVID pandemic, life
expectancy at birth decreased from 84.73 years for females and 81.34
years for males in 2019 to 84.29/80.60 years in 2020. For 2021, the
numbers were again 84.82/81.21 years. This reflects, of course, the
decreased COVID mortality due to vaccination. Moreover, the flu A(H3N2)
wave, which peaked around Christmas with rather high rates of illness
among young people, did not cause substantial excess mortality, which
may, in part, be due to people with respiratory symptoms having less
contacts than usual with older people and other risk groups.

The increase in life expectancy in Sweden, and many other countries, up
until the mid-20th century was largely driven by decreasing childhood
mortality, which also caused changes in the cause of death patterns,
with directly communicable diseases becoming less common relative to
age-related diseases, such as circulatory diseases and cancer. In
contrast, the continued increase in rich countries after that, which was
temporarily interrupted by the pandemic, is largely due to decreased
mortality at older ages.

Vishnevsky (2017) discusses the development in life expectancy and causes
of death after 1960 in Russia, compared to high-income countries, in
particular Western European countries. In the EU-15 countries,
age-standardised mortality rates from circulatory, external and
respiratory causes have decreased greatly since 1970, while cancer
mortality has decreased modestly. The proportion of deaths from
circulatory causes has also decreased (from nearly 50 percent to about
30 percent), while the proportion of deaths from cancer has increased
(from about 20 percent to about 30 percent). No such changes have
occurred in Russia, where life expectancy has not improved much since
the 1960s (although it has improved relative to the dramatic increases
in mortality during the 1990s).

From this, one might conclude that the increased life expectancy in rich
countries largely has been about decreased circulatory mortality.
However, Vishnevsky points out that focusing on standardised rates for
all ages hides a significant increase in life expectancy for those dying
also of non-circulatory causes. In Sweden, for example, the life
expectancy for people dying of cancer or other neoplasms increased 8.2
years for females and 7.6 years for males during to period 1960–2010.
The corresponding increase for circulatory diseases (where life
expectancy was higher than for cancer already in 1960) is 8.0/6.8 years.
It is clear that this reflects a marked decrease in cancer mortality at
young ages, a point similar to what has been made earlier by researchers
like Riggs (1994).

One factor not discussed by Vishnevsky is the impact of changing
practices in reporting causes of death over a long time. For example,
the increase in life expectancy has been particularly strong for the
residual category, other diseases, in Sweden, with 20.0 years for
females and 17.8 years for males. This category includes dementia, which
was a rare underlying cause of death in 1960. Back then, most people
with dementia probably had circulatory or respiratory causes reported
instead, and the other category was dominated by other causes, with a
much lower life expectancy.

In light of this, it may be interesting to compare the correlation
between general life expectancy and proportion of deaths ascribed to
different causes in varying countries more in detail. I made a Julia
package, MortIntl, which can be
used to analyse such trends, based on cause-specific mortality data from
WHO (2022) and life tables from University of California, Berkeley and Max Planck Institute for Demographic Research (2022). It uses a configuration similar to
my earlier Mortchartgen,
which I have used to generate Mortality
Charts
, but extracts data directly from
the data files using AWK instead of relying on a SQL database.

Fig. 1 and fig. 2 show female and male life expectancy at birth in
relation to proportion of deaths from circulatory causes (as defined for
Mortality Charts) for
the Nordic and Baltic countries, with Iceland excluded due to small
population.1

Figure 1: Circulatory deaths vs life expectancy females Nordic and Baltic countries.
Figure 2: Circulatory deaths vs life expectancy males Nordic and Baltic countries.

The charts clearly show that improvements in life expectancy continued
for a long time among, for example, females in Finland and Sweden, after
circulatory causes became dominant, without any substantial change in
the proportion of deaths ascribed to these causes. That proportion
really started decreasing after the 1980s, when dementia became more
commonly reported (see Mortality
Charts
).

The Baltic countries, especially Estonia, have in recent years attained
a female life expectancy close to the Nordic countries, but the
proportion of circulatory deaths there is higher than it has been in the
Nordic countries at any point in time. In contrast, Denmark, has had a
lower proportion of circulatory deaths than the other Nordic countries,
a pattern which has been more pronounced in recent decades. The
difference in circulatory deaths between Denmark and Estonia in recent
years, when both have had similar life expectancy among females, is
greater than the temporal variation, over nearly 70 years, in any of the
Nordic countries.

From this, it seems that clear that great caution is warranted in
drawing any epidemiological conclusions from trends for officially
reported circulatory mortality over all ages.

References

Riggs, J. E. 1994. “The cohort mortality perspective: The emperor′s new clothes of epidemiology, an illustration using cancer mortality.” Regulatory Toxicology and Pharmacology 19 (2): 202–210. doi:10.1006/rtph.1994.1018.
Statistics Sweden. 2022. “Life table by sex and age.” https://www.statistikdatabasen.scb.se/goto/en/ssd/LivslangdEttariga.
University of California, Berkeley and Max Planck Institute for Demographic Research. 2022. “Coronavirus (COVID-19) infection survey, UK: 7 january 2022.” https://www.mortality.org.
Vishnevsky, Anatoly. 2017. “Mortality in russia: The second epidemiological revolution that never was.” Demographic Review 2 (5): 4–33. doi:10.17323/demreview.v2i5.5581.
WHO. 2022. “WHO Mortality Database.” https://www.who.int/data/data-collection-tools/who-mortality-database.

  1. The charts can be generated by cloning the blog
    repository
    , installing
    MortIntl with the relevant data files, as described in the
    documentation, and running circall_e0_baltnord.jl in the
    subdirectory postdata/2022-03-27-transition.↩︎

Life expectancy and transition in cause of death patterns

By: Karl Pettersson

Re-posted from: https://www.dusty-test.klpn.se/posts/2022-03-27-transition.html

Life expectancy and transition in cause of death patterns

Posted on 2022-03-27

by Karl Pettersson.

Tags: ,

This week, the Swedish statistical agency has published life tables for
Sweden 2021 (Statistics Sweden 2023). With the first waves of the COVID pandemic, life
expectancy at birth decreased from 84.73 years for females and 81.34
years for males in 2019 to 84.29/80.60 years in 2020. For 2021, the
numbers were again 84.82/81.21 years. This reflects, of course, the
decreased COVID mortality due to vaccination. Moreover, the flu A(H3N2)
wave, which peaked around Christmas with rather high rates of illness
among young people, did not cause substantial excess mortality, which
may, in part, be due to people with respiratory symptoms having less
contacts than usual with older people and other risk groups.

The increase in life expectancy in Sweden, and many other countries, up
until the mid-20th century was largely driven by decreasing childhood
mortality, which also caused changes in the cause of death patterns,
with directly communicable diseases becoming less common relative to
age-related diseases, such as circulatory diseases and cancer. In
contrast, the continued increase in rich countries after that, which was
temporarily interrupted by the pandemic, is largely due to decreased
mortality at older ages.

Vishnevsky (2017) discusses the development in life expectancy and causes
of death after 1960 in Russia, compared to high-income countries, in
particular Western European countries. In the EU-15 countries,
age-standardised mortality rates from circulatory, external and
respiratory causes have decreased greatly since 1970, while cancer
mortality has decreased modestly. The proportion of deaths from
circulatory causes has also decreased (from nearly 50 percent to about
30 percent), while the proportion of deaths from cancer has increased
(from about 20 percent to about 30 percent). No such changes have
occurred in Russia, where life expectancy has not improved much since
the 1960s (although it has improved relative to the dramatic increases
in mortality during the 1990s).

From this, one might conclude that the increased life expectancy in rich
countries largely has been about decreased circulatory mortality.
However, Vishnevsky points out that focusing on standardised rates for
all ages hides a significant increase in life expectancy for those dying
also of non-circulatory causes. In Sweden, for example, the life
expectancy for people dying of cancer or other neoplasms increased 8.2
years for females and 7.6 years for males during to period 1960–2010.
The corresponding increase for circulatory diseases (where life
expectancy was higher than for cancer already in 1960) is 8.0/6.8 years.
It is clear that this reflects a marked decrease in cancer mortality at
young ages, a point similar to what has been made earlier by researchers
like Riggs (1994).

One factor not discussed by Vishnevsky is the impact of changing
practices in reporting causes of death over a long time. For example,
the increase in life expectancy has been particularly strong for the
residual category, other diseases, in Sweden, with 20.0 years for
females and 17.8 years for males. This category includes dementia, which
was a rare underlying cause of death in 1960. Back then, most people
with dementia probably had circulatory or respiratory causes reported
instead, and the other category was dominated by other causes, with a
much lower life expectancy.

In light of this, it may be interesting to compare the correlation
between general life expectancy and proportion of deaths ascribed to
different causes in varying countries more in detail. I made a Julia
package, MortIntl, which can be
used to analyse such trends, based on cause-specific mortality data from
WHO (2025) and life tables from University of California, Berkeley and Max Planck Institute for Demographic Research (2022). It uses a configuration similar to
my earlier Mortchartgen,
which I have used to generate Mortality
Charts
, but extracts data directly from
the data files using AWK instead of relying on a SQL database.

Fig. 1 and fig. 2 show female and male life expectancy at birth in
relation to proportion of deaths from circulatory causes (as defined for
Mortality Charts) for
the Nordic and Baltic countries, with Iceland excluded due to small
population.1

Figure 1: Circulatory deaths vs life expectancy females Nordic and Baltic countries.
Figure 2: Circulatory deaths vs life expectancy males Nordic and Baltic countries.

The charts clearly show that improvements in life expectancy continued
for a long time among, for example, females in Finland and Sweden, after
circulatory causes became dominant, without any substantial change in
the proportion of deaths ascribed to these causes. That proportion
really started decreasing after the 1980s, when dementia became more
commonly reported (see Mortality
Charts
).

The Baltic countries, especially Estonia, have in recent years attained
a female life expectancy close to the Nordic countries, but the
proportion of circulatory deaths there is higher than it has been in the
Nordic countries at any point in time. In contrast, Denmark, has had a
lower proportion of circulatory deaths than the other Nordic countries,
a pattern which has been more pronounced in recent decades. The
difference in circulatory deaths between Denmark and Estonia in recent
years, when both have had similar life expectancy among females, is
greater than the temporal variation, over nearly 70 years, in any of the
Nordic countries.

From this, it seems that clear that great caution is warranted in
drawing any epidemiological conclusions from trends for officially
reported circulatory mortality over all ages.

References

Riggs, J. E. 1994. “The cohort mortality perspective: The emperor′s new clothes of epidemiology, an illustration using cancer mortality.” Regulatory Toxicology and Pharmacology 19 (2): 202–210. doi:10.1006/rtph.1994.1018.
Statistics Sweden. 2023. “Life table by sex and age.” https://www.statistikdatabasen.scb.se/goto/en/ssd/LivslangdEttariga.
University of California, Berkeley and Max Planck Institute for Demographic Research. 2022. Human Mortality Database.” https://www.mortality.org.
Vishnevsky, Anatoly. 2017. “Mortality in russia: The second epidemiological revolution that never was.” Demographic Review 2 (5): 4–33. doi:10.17323/demreview.v2i5.5581.
WHO. 2025. “WHO Mortality Database.” https://www.who.int/data/data-collection-tools/who-mortality-database.

  1. The charts can be generated by cloning the blog
    repository
    , installing
    MortIntl with the relevant data files, as described in the
    documentation, and running circall_e0_baltnord.jl in the
    subdirectory postdata/2022-03-27-transition.↩︎

Back to the basics: array literals in Julia

By: Blog by Bogumił Kamiński

Re-posted from: https://bkamins.github.io/julialang/2022/03/25/arrays.html

Introduction

Array literals are one of the basic constructs in the Julia language
that essentially every developer learns during the first session.
However, the exact mechanism of how these literals work are complex if one
wants to understand them in full.

In this post I want to give several examples how the array literals in Julia
work and highlight upcoming changes in the Julia language that might affect
your code, if you happen to use them.

Expect that the material I present today is more advanced than usual,
but I think the topics I cover are relevant for any Julia developer writing
production code.

This post was written using Julia 1.7.0 and Julia 1.9.0-DEV.245.

The rules

In the Julia Manual section on array literals you can read that:

Arrays can also be directly constructed with square braces;
the syntax [A, B, C, ...] creates a one dimensional array (i.e., a vector)
containing the comma-separated arguments as its elements.
The element type (eltype) of the resulting array is automatically determined
by the types of the arguments inside the braces.
If all the arguments are the same type, then that is its eltype.
If they all have a common promotion type then they get converted to that type
using convert and that type is the array’s eltype.
Otherwise, a heterogeneous array that can hold anything — a Vector{Any}
is constructed; this includes the literal [] where no arguments are given.

In short this rule means that:

  • when you write [A, B, C] Julia checks types of A, B, and C;
  • if these types are equal then a Vector having this type is created;
  • if these types are not equal but can be promoted to a common type then
    this common type is used as element type of an array;
  • otherwise an Any element type is used.

Let us see these rules in action.

The first case is when all passed elements have the same element type:

julia> [1, 2, 3]
3-element Vector{Int64}:
 1
 2
 3

Here we passed three integers, so element type of created vector is Int64.

Now let us see a second rule at work. We pass some values that have a common
promotion type:

julia> [true, 2.0, 3]
3-element Vector{Float64}:
 1.0
 2.0
 3.0

We have passed a Bool, Float64, and Int64 value and they all got converted
to Float64, because it is their common promotion type.

Sometimes the result can have a type that is not even any of the types of passed
elements:

julia> [big(1), 2.0, 3.0]
3-element Vector{BigFloat}:
 1.0
 2.0
 3.0

This time we get a conversion to BigFloat as this is a common promotion type
of BigInt and Float64.

Finally sometimes a common promotion type is not concrete in which case
no conversion takes place:

julia> [[1, 2], ["a", "b"]]
2-element Vector{Vector}:
 [1, 2]
 ["a", "b"]

In this case Vector{Int64} and Vector{String} have a common promotion type
Vector which is UnionAll so no conversion of elements occurred.

The last case in our set of rules was that there is no common promotion type
for the elements of created vector. In this case the result has Any element
type:

julia> [1, "2", '3']
3-element Vector{Any}:
 1
  "2"
  '3': ASCII/Unicode U+0033 (category Nd: Number, decimal digit)

In this example Int64, String, and Char do not have a common promotion
type.

Upcoming changes in how array literals work

The codes given above work the same way under Julia 1.7 and Julia nightly.
Now we are getting to a territory when differences will be present.

Start a Julia 1.7 session in your terminal and run the following code:

julia> [1:2, [1, 2]]
2-element Vector{AbstractVector{Int64}}:
 1:2
 [1, 2]

julia> [1:2, ["a", "b"]]
2-element Vector{AbstractVector}:
 1:2
 ["a", "b"]

As you can see the resulting array has an abstract element type.
In particular no conversion of the elements of the array literal happened.
The reason of this behavior is the return value of the promote_type function:

julia> promote_type(typeof(1:2), typeof([1, 2]))
AbstractVector{Int64} (alias for AbstractArray{Int64, 1})

julia> promote_type(typeof(1:2), typeof(["a", "b"]))
AbstractVector (alias for AbstractArray{T, 1} where T)

As you can see the result of the pomote_type is used as an element type of the
created vectors.

Now switch to current Julia nightly build (I used Julia 1.9.0-DEV.245) and
run the same code:

julia> [1:2, [1, 2]]
2-element Vector{Vector{Int64}}:
 [1, 2]
 [1, 2]

julia> [1:2, ["a", "b"]]
2-element Vector{Vector{Any}}:
 [1, 2]
 ["a", "b"]

As you can see the result is different. Now elements of array literals are
converted to concrete Vector{Int64} and Vector{Any} types respectively.

Let us check the result of promote_type:

julia> promote_type(typeof(1:2), typeof([1, 2]))
Vector{Int64} (alias for Array{Int64, 1})

julia> promote_type(typeof(1:2), typeof(["a", "b"]))
Vector{Any} (alias for Array{Any, 1})

It looks that promotion rules have hanged since Julia 1.7. Indeed they were
updated in this PR. The PR introduces a rule that states that if no
promote_rule is defined for two arrays then typejoin on their eltypes is run
instead and an Array having this element type as the container is returned.
Here is the crucial line of code that was added in this PR:

promote_result(::Type{<:AbstractArray{T,n}}, ::Type{<:AbstractArray{S,n}},
               ::Type{Bottom}, ::Type{Bottom}) where {T,S,n} =
    (@inline; Array{promote_type(T,S),n})

However, let me recall you the earlier example we have given
(run on Julia nightly):

julia> [[1, 2], ["a", "b"]]
2-element Vector{Vector}:
 [1, 2]
 ["a", "b"]

Since this time a common promotion type exists, and is Vector
no conversion is done and the resulting container does not have a concrete
element type (as opposed to e.g. [1:2, ["a", "b"]] shown above).

Why is this relevant? As you can see, when you migrate from Julia 1.7 to
any Julia release of Julia that has this PR incorporated you must be aware
that your old code might stop working the same way it did if it used
array literals that contained arrays inside.

Conclusions

There is one key takeaway from my examples.

If in your current code you use array literals to store arrays inside them
please review them before upgrading to latest Julia.

In particular if you do not want an implicit conversion use an element type
prefix in front of the array literal for example like this:

julia> Any[[1, 2], ["a", "b"]]
2-element Vector{Any}:
 [1, 2]
 ["a", "b"]

julia> AbstractVector[1:2, [1, 2]]
2-element Vector{AbstractVector}:
 1:2
 [1, 2]

Passing an explicit element type prefix is a practice that I have currently
adopted in all of my production codes as this is the safest way to make sure my
programs run exactly as I want.