This is a little Bayesian analysis of part of the Daniela Poggiali case. Work in progress…
According to a statistical report by Franco Tagliaro, professor in forensic medicine at the university of Verona, and Rocco Micciolo, professor in epidemiology at the university of Trento, nurse Daniela killed about 80 patients in a two year period in the Umberto I hospital in Lugo (in the province of Ravenna). In the sequel I will refer to to Tagliaro and Micciolo as TM. The case became well known in Italy. One may watch an episode of the Netflix series “Nurses who kill” devoted to her case. The summary of that episode on th IMDb website says “Italian nurse and ward joker Daniela Poggiali was present at over 90 deaths in just two years. The same time deaths were happening a string of petty thefts were also taking place. The thefts cause suspicious fellow health workers to call in the police. To add even more intrigue to the larger than life nurse, photos of her posing with dead bodies were found on her phone. The patients that had died she said were ‘irritating’.” Indeed, that is the story propagated by the gutter press and also by hospital authorities, prosecutors and prosecution experts.
TM actually study two sources of evidence. One, on the Tagliaro’s pathology/toxicology side, is the post-mortem concentration of potassium in the “vitreous humour” (the thin watery fluid in your eye) of an elderly and terminally ill patient, Mrs Calderoni, a number of hours after her otherwise not unexpected decease. The other, on Micciolo’s epidemiology side, is data on deaths during Daniela’s shifts and not during her shifts. It seems that the death rate is elevated by a factor of about 2 when she is on duty. An association is clear, but the cause need not be activities of a serial killer.
After death, cell-walls throughout the body start to break down and substances in the cells of specialised organs start to diffuse throughout the body. Potassium levels in the vitreous fluid increase, and this increase has in the past been used to estimate the time of death from post-morten measurements. This use is however very controversial in view of small amounts of data and huge statistical variation due, among other things, to age, sex, and health status, of the persons concerned. Forensic scientists have collected data from criminal cases in which a victim was suddenly murdered in a violent way. They also have data from one or two accidental (medical error) or deliberate (suicide) cases. Almost no data pertains to persons similar to Rosa Calderoni: at the end of her natural life, not in intensive care but in a normal care ward, mostly populated by patients like her: elderly and terminally ill. In this case, Calderoni’s post-portem potassium levels are is being used to estimate the concentration at death, in order to argue that it is so high that Calderoni must have been murdered by Daniela Poggiali by an injection (or infusion) of Potassium chloride. The statistics of that computation is even more controversial than the usual one, especially since ``natural’’ organ failure and death agony can also cause potassium levels in the body to increase sharply around the time of death.
Note: Daniela is presently being tried for the charge of murder of one patient only, Rosa Calderoni, age 78. The statistical evidence that her presence in the hospital was associated with a higher death-rate of patients is supposed to support the prosecution’s argument that she is a cold-blooded killer, hence that the death of Rosa Calderoni was likely caused by her.
Here is the basic statistical proof of association between Daniela’s presence and deaths of patients on the wards where she worked. Numbers of deaths, and time periods. Supposedly, the selection of this data has taken care of confounding factors like time of year, number of patients on the ward, time of day, day of week, number of nurses on the wards, severity of their illnesses, and so on. But that is another story. I believe that the time periods here are measured in numbers of hours. Then we are talking about 180 days, and we are restricting attention to days when Danila does work; on such days she does of course tend only to work one 8 hour shift, so about one third of the time.
(916 + 3452)/24
## [1] 182
poisson.test(x = c(42, 85), T = c(916, 3452))
##
## Comparison of Poisson rates
##
## data: c(42, 85) time base: c(916, 3452)
## count1 = 42, expected count1 = 26.633, p-value = 0.001456
## alternative hypothesis: true rate ratio is not equal to 1
## 95 percent confidence interval:
## 1.254907 2.725692
## sample estimates:
## rate ratio
## 1.862111
The p-value for an elevated rate is 1 in a thousand. The other source of evidence is the post-mortem concentration of Potassium in the ocular fluid of an elderly and terminally ill patient, Mrs Calderoni, a number of hours after her otherwise not unexpected decease.
In this R markdown document I only represent a subjective Bayesian account of part of this case.
If TM are right then Daniela Poggiali is actually the cause of about 80 excess deaths. Of course, maybe she killed out of pity for the suffering of terminally ill patients, but anyway, since this was not authorised by medical specialists or family members or indeed the patient, it is a terrible crime. It is a crime which would make her the almost the most deadly health-care serial killer (HCSK) ever. That would not only be horrfic. It would also be amazing. I estimate the chance that a particular nurse in Italy is the most deadly HCSK ever at about 1 in a million.
Of course, nurses also do occasionally kill patients, whether our of pity, personal animosity, or for personal gain (the opportunity to steal some gold trinkets), or whatever. I estimate the rate of killings of this kind at perhaps 1 per 1000 deaths. Note that it is certainly the case that most deaths in hospitals are due to medical errors, though mainly in situations where the patient was expected to die soon, anyway. For various reasons, mainly economic, medical errors are hardly ever admitted and even less often registered as such. The legal and economic consequences are too great – and some rate of error is of course unavoidable.
library(Rgraphviz)
## Loading required package: graph
## Loading required package: BiocGenerics
## Loading required package: parallel
##
## Attaching package: 'BiocGenerics'
## The following objects are masked from 'package:parallel':
##
## clusterApply, clusterApplyLB, clusterCall, clusterEvalQ,
## clusterExport, clusterMap, parApply, parCapply, parLapply,
## parLapplyLB, parRapply, parSapply, parSapplyLB
## The following objects are masked from 'package:stats':
##
## IQR, mad, sd, var, xtabs
## The following objects are masked from 'package:base':
##
## anyDuplicated, append, as.data.frame, basename, cbind, colnames,
## dirname, do.call, duplicated, eval, evalq, Filter, Find, get, grep,
## grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
## order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
## rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply,
## union, unique, unsplit, which, which.max, which.min
## Loading required package: grid
library(gRain)
## Loading required package: gRbase
yn <- c("yes", "no")
DS <-cptable(~DanielaSerialKiller, levels = yn, values = c(1, 10^6 - 1))
DC.DS <- cptable(~DanielaKilledCalderoni|DanielaSerialKiller, levels = yn, values = c(10^2 - 1, 1, 1, 10^3 - 1))
TM.DS <- cptable(~TMconcludeDSK|DanielaSerialKiller, levels = yn, values = c(10^3 - 1, 1, 1, 10^3 - 1))
daniela_cpt <- compileCPT(DS, DC.DS, TM.DS)
daniela_cpt
## cpt_spec with probabilities:
## P( DanielaSerialKiller )
## P( DanielaKilledCalderoni | DanielaSerialKiller )
## P( TMconcludeDSK | DanielaSerialKiller )
daniela_cpt$DanielaSerialKiller
## DanielaSerialKiller
## yes no
## 0.000001 0.999999
daniela_cpt$DanielaKilledCalderoni
## DanielaSerialKiller
## DanielaKilledCalderoni yes no
## yes 0.99 0.001
## no 0.01 0.999
daniela_cpt$TMconcludeDSK
## DanielaSerialKiller
## TMconcludeDSK yes no
## yes 0.999 0.001
## no 0.001 0.999
daniela_bn <- grain(daniela_cpt)
daniela_bn <- propagate(daniela_bn)
querygrain(daniela_bn, nodes = c("DanielaSerialKiller", "DanielaKilledCalderoni"), type = "joint")
## DanielaKilledCalderoni
## DanielaSerialKiller yes no
## yes 0.000000990 0.00000001
## no 0.000999999 0.99899900
daniela_bn2 <- setEvidence(daniela_bn, evidence = list(TMconcludeDSK = "yes"))
querygrain(daniela_bn2, nodes = c("DanielaSerialKiller", "DanielaKilledCalderoni"), type = "joint")
## DanielaKilledCalderoni
## DanielaSerialKiller yes no
## yes 0.000988024 9.98004e-06
## no 0.000999002 9.98003e-01
saveHuginNet(daniela_bn, "daniela.net")
plot(daniela_bn)