Some Graphs about COVID-19 in Italy

Table of Contents



This page contains various plots generated from that data using Org Mode and R: no fancy web services, just plain-old off-line generation. On top of being an interesting exercise on R and literate programming in Emacs, I use this page to get an idea of the evolution of the pandemic in Italy.

This page was created on <2020-03-28 Sat> and last updated on <2021-02-27 Sat>.

The source code available on the COVID-19 pages is distributed under the MIT License; the content is distributed under a Creative Commons - Attribution 4.0.

Get the data

First we get the data:

curl > data/dpc-covid19-ita-regioni.csv
curl > data/dpc-covid19-ita-andamento-nazionale.csv 
curl > data/dpc-covid19-ita-regioni-latest.csv

R Functions

This section contains the code for plotting data. The function my_plot plots different variables of an input dataframe over time, optionally filtering over region, which is the denomination of an Italian region.

The optional argument max defines the maximum value for the x-axis, while the optional Boolean arguments textlabels and filter control, respectively, whether text labels are printed on graphs and data has to be filtered by Region.

Finally, the optional arguments variables, graphtypes, and colors are vectors, defining, respectively, the variables to plot, the type of plot, and the colors used.

my_plot <- function(region, data, max=-1, textlabels=TRUE, filter=FALSE, 
                    variables  = c("totale_casi", "nuovi_positivi", "totale_positivi", "deceduti", "dimessi_guariti"),
                    graphtypes = c("l", "h", "l", "l", "l"),
                    colors     = c("red", "black", "orange", "slategrey", "forestgreen")) {
  par(cex=1.40, las=2)

  # if asked to filter, filter data according to region
  if (filter) {
    dataframe <- subset(data, denominazione_regione == region)
  else {
    dataframe <- data

  if (max == -1) {

       xlim=c(min(data$data), max(data$data)),
       main = region,

  axis.Date(1, at=dataframe$data, by="days", format="%b %d")

  # do the plots, now
  for (i in 1:length(variables)) {
    lines(x=dataframe$data, y=dataframe[, variables[i]],
    if (textlabels) {
      text(x=dataframe$data, y=dataframe[, variables[i]], 
           label=dataframe[, variables[i]], 

  values = sprintf("(%s)", dataframe[nrow(dataframe), variables])
  legend("topleft", legend=paste(variables, values), col=colors, lty=1, cex=1.6)
  grid(col = "lightgray")

Then we read the data from the CSV files of the Civil Protection repository:


# evolution over time, by Region
data = read.csv(file.path(PATH, "dpc-covid19-ita-regioni.csv"))
data$data <- as.Date(data$data)

# evolution over time at the National level
national = read.csv(file.path(PATH, "dpc-covid19-ita-andamento-nazionale.csv"))
national$data <- as.Date(national$data)

# latest regional data
latest = read.csv(file.path(PATH, "dpc-covid19-ita-regioni-latest.csv"))
latest$data <- as.Date(national$data)

We are now ready to print and plot the data.

This Week in Italy

cols = c(
labels = c(
  "In hospitals with symptoms", 
  "In ICUs",
  "Total hospitalized",
  "Quarantined at home", 
  "Active cases",
  "New cases",
  "Total number of cases"

Today = unlist(national[nrow(national), cols])
Yesterday = unlist(national[nrow(national) - 1, cols])
TwoDaysAgo = unlist(national[nrow(national) - 2, cols])
ThreeDaysAgo = unlist(national[nrow(national) - 3, cols])
FourDaysAgo = unlist(national[nrow(national) - 4, cols])
FiveDaysAgo = unlist(national[nrow(national) - 5, cols])

output_frame <- data.frame(labels, FiveDaysAgo, FourDaysAgo, ThreeDaysAgo, TwoDaysAgo, Yesterday, Today)
colnames(output_frame) <- rev(seq(Sys.Date(), by="-1 day", length.out=7))
colnames(output_frame)[1] <- "Label"
Label 2021-04-09 2021-04-10 2021-04-11 2021-04-12 2021-04-13 2021-04-14
In hospitals with symptoms 28146 27654 27251 27329 26952 26369
In ICUs 3603 3588 3585 3593 3526 3490
Total hospitalized 31749 31242 30836 30922 30478 29859
Quarantined at home 504612 501843 502169 493495 488742 484801
Active cases 536361 533085 533005 524417 519220 514660
New cases 18938 17567 15746 9789 13447 16168
Recovered 3086586 3107069 3122555 3140565 3158725 3178976
Deaths 113579 113923 114254 114612 115088 115557
Total number of cases 3736526 3754077 3769814 3779594 3793033 3809193

Variations with respect to previous day

We now plot the variations in the last week, that is the difference between a day and the previous day. In many cases, the lower the number, the better. In other cases (e.g., Recovered), the higher, the better.

Diff4 = FourDaysAgo - FiveDaysAgo
Diff3 = ThreeDaysAgo - FourDaysAgo
Diff2 = TwoDaysAgo - ThreeDaysAgo 
Diff1 = Yesterday - TwoDaysAgo
Diff0 = Today - Yesterday

diff_frame <- data.frame(labels, Diff4, Diff3, Diff2, Diff1, Diff0)
labels Diff4 Diff3 Diff2 Diff1 Diff0
In hospitals with symptoms -492 -403 78 -377 -583
In ICUs -15 -3 8 -67 -36
Total hospitalized -507 -406 86 -444 -619
Quarantined at home -2769 326 -8674 -4753 -3941
Active cases -3276 -80 -8588 -5197 -4560
New cases -1371 -1821 -5957 3658 2721
Recovered 20483 15486 18010 18160 20251
Deaths 344 331 358 476 469
Total number of cases 17551 15737 9780 13439 16160

See also the historical series of new cases in Italy.

Situation in Italy

Overall Situation

Evolution over time.

my_plot("Italia", national, textlabels=FALSE)


Breakdown of Quarantine

It tells where people with COVID-19 are spending their quarantine, that is, a breakdown of the “yellow” line of the previous plot.

The blue line is the number of people hospedalized during the (first) lockdown. Now the capacity of the health system should be higher, but it seems something to look at (although the situation differs from region to region).

          max(national$isolamento_domiciliare), textlabels=FALSE, filter=FALSE, 
          variables=c("ricoverati_con_sintomi", "terapia_intensiva", "totale_ospedalizzati", "isolamento_domiciliare"),  
          graphtypes=c("l", "l", "l", "l", "h"),
          colors=c("#FECEAB", "#EC2049", "#E84A5F", "#A7226E"))
abline(h = national[38,]$totale_ospedalizzati, col="#330000", lwd=2, lty=3)
abline(v = as.Date("2020-10-25"), col="#330000", lwd=2, lty=3)


Focus on Trentino, Liguria, Veneto and Lombardia

Situation in Trentino

my_plot("P.A. Trento", data, filter=TRUE, textlabels=FALSE)


Situation in Liguria

my_plot("Liguria", data, filter=TRUE, textlabels=FALSE)


Situation in Veneto

my_plot("Veneto", data, filter=TRUE, textlabels=FALSE)


Situation in Lombardia

my_plot("Lombardia", data, filter=TRUE, textlabels=FALSE)


Situation by Region

Situation by Region

# how many rows and columns?
par(mfrow=c(11, 2))

max <- max(data$totale_casi)

regions <- c("Valle d'Aosta", "Piemonte", "Liguria", "Lombardia", "Veneto",
             "P.A. Trento", "P.A. Bolzano", "Friuli Venezia Giulia",
             "Emilia-Romagna", "Toscana", "Marche", "Umbria",
             "Lazio", "Abruzzo", "Molise", "Campania",
             "Puglia", "Basilicata", "Calabria", "Sicilia",
for (region in regions) {
    region, data, filter=TRUE, textlabels=FALSE,
          variables=c("totale_casi", "totale_positivi", "deceduti", "dimessi_guariti"),
          max = max,
          graphtypes=c("l", "l", "l", "l"),
          colors=c("red", "orange", "slategrey", "forestgreen"))