A Small Margin Of Error
Guest Post by Willis Eschenbach
I see that Zeke Hausfather and others are claiming that 2018 is the warmest year on record for the ocean down to a depth of 2,000 metres. Here’s Zeke’s claim:
Figure 1. Change in ocean heat content, 1955 – 2018. Data available from Institute for Applied Physics (IAP).
When I saw that graph in Zeke’s tweet, my bad-number detector started flashing bright red. What I found suspicious was that the confidence intervals seemed far too small. Not only that, but the graph is measured in a unit that is meaningless to most everyone. Hmmm …
Now, the units in this graph are “zetajoules”, abbreviated ZJ. A zetajoule is a billion trillion joules, or 1E+21 joules. I wanted to convert this to a more familiar number, which is degrees Celsius (°C). So I had to calculate how many zetajoules it takes to raise the temperature of the top two kilometres of the ocean by 1°C.
I go over the math in the endnotes, but suffice it to say at this point that it takes about twenty-six hundred zetajoules to raise the temperature of the top two kilometres of the ocean by 1°C. 2,600 ZJ per degree.
Now, look at Figure 1 again. They claim that their error back in 1955 is plus or minus ninety-five zetajoules … and that converts to ± 0.04°C. Four hundredths of one degree celsius … right …
Call me crazy, but I do NOT believe that we know the 1955 temperature of the top two kilometres of the ocean to within plus or minus four hundredths of one degree.
It gets worse. By the year 2018, they are claiming that the error bar is on the order of plus or minus nine zetajoules … which is three thousandths of one degree C. That’s 0.003°C. Get real! Ask any process engineer—determining the average temperature of a typcial swimming pool to within three thousandths of a degree would require a dozen thermometers or more …
The claim is that they can achieve this degree of accuracy because of the ARGO floats. These are floats that drift down deep in the ocean. Every ten days they rise slowly to the surface, sampling temperatures as they go. At present, well, three days ago, there were 3,835 Argo floats in operation.
Figure 2. Distribution of all Argo floats which were active as of January 8, 2019.
Looks pretty dense-packed in this graphic, doesn’t it? Maybe not a couple dozen thermometers per swimming pool, but dense … however, in fact, that’s only one Argo float for every 133,000 square km (50,000 square miles) of ocean. That’s a box that’s 365 km (220 miles) on a side and two km (1.2 miles) deep … containing one thermometer.
Here’s the underlying problem with their error estimate. As the number of observations goes up, the error bar decreases by one divided the square root of the number of observations. And that means if we want to get one more decimal in our error, we have to have a hundred times the number of data points.
For example, if we get an error of say a tenth of a degree C from ten observations, then if we want to reduce the error to a hundredth of a degree C we need one thousand observations …
And the same is true in reverse. So let’s assume that their error estimate of ± 0.003°C for 2018 data is correct, and it’s due to the excellent coverage of the 3,835 Argo floats.
That would mean that we would have an error of ten times that, ± 0.03°C if there were only 38 ARGO floats …
Sorry. Not believing it. Thirty-eight thermometers, each taking three vertical temperature profiles per month, to measure the temperature of the top two kilometers of the entire global ocean to plus or minus three hundredths of a degree?
My bad number detector was still going off. So I decided to do a type of “Monte Carlo” analysis. Named after the famous casino, a Monte Carlo analysis implies that you are using random data in an analysis to see if your answer is reasonable.
In this case, what I did was to get gridded 1° latitude by 1° longitude data for ocean temperatures at various depths down to 2000 metres from the Levitus World Ocean Atlas. It contains the long-term monthly averages at each depth for each gridcell for each month. Then I calculated the global monthly average for each month from the surface down to 2000 metres.
Now, there are 33,713 1°x1° gridcells with ocean data. (I excluded the areas poleward of the Arctic/Antarctic Circles, as there are almost no Argo floats there.) And there are 3,825 Argo floats. On average some 5% of them are in a common gridcell. So the Argo floats are sampling on the order of ten percent of the gridcells … meaning that despite having lots of Argo floats, still at any given time, 90% of the 1°x1° ocean gridcells are not sampled. Just sayin …
To see what difference this might make, I did repeated runs by choosing 3,825 ocean gridcells at random. I then ran the same analysis as before—get the averages at depth, and then calculate the global average temperature month by month for just those gridcells. Here’s a map of typical random locations for simulated Argo locations for one run.
Figure 3. Typical simulated distribution of Argo floats for one run of Monte Carlo Analysis.
And in the event, I found what I suspected I’d find. Their claimed accuracy is not borne out by experiment. Figure 4 shows the results of a typical run. The 95% confidence interval for the results varied from 005°C to 0.1°C.
Figure 4. Typical run, average global ocean temperature 0-2,000 metres depth, from Levitus World Ocean Atlas (red dots) and from 3.825 simulated Argo locations. White “whisker” lines show the 95% confidence interval (95%CI). For this run, the 95%CI was 0.07°C. Small white whisker line at bottom center shows the claimed 2018 95%CI of ± 0.003°C.
As you can see, using the simulated Argo locations gives an answer that is quite close to the actual temperature average. Monthly averages are within a tenth of a degree of the actual average … but because the Argo floats only measure about 10% of the 1°x1° ocean gridcells, that is still more than an order of magnitude larger than the claimed 2018 95% confidence interval for the AIP data shown in Figure 1.
So I guess my bad number detector must still be working …
Finally, Zeke says that the ocean temperature in 2018 exceeds that in 2017 by “a comfortable margin”. But in fact, it is warmer by only 8 zetajoules … which is less than the claimed 2018 error. So no, that is not a “comfortable margin”. It’s well within even their unbelievably small claimed error, which they say is ± 9 zetajoules for 2018.
In closing, please don’t rag on Zeke about this. He’s one of the good guys, and all of us are wrong at times. As I myself have proven more often than I care to think about, the American scientist Lewis Thomas was totally correct when he said, “We are built to make mistakes, coded for error” …
Best regards to everyone,
PS—when commenting please quote the exact words that you are discussing. That way we can all understand both who and what you are referring to.
Math Notes: Here is the calculation of the conversion of zetajoules to degrees of warming of the top two km of the ocean. I work in the computer language R, and these are the actual calculations. Everything after a hashmark (#) in a line is a comment.
heatcapacity=sw_cp(t=4,p=100) # heat capacity, with temperature and pressure at 1000 m depth print(paste(round(heatcapacity), "joules/kg/°C"))  "3958 joules/kg/°C" seadensity=gsw_rho(35,4,1000) # density, with temperature and pressure at 1000 m depth print(paste(round(seadensity), "kg/cubic metre"))  "1032 kg/cubic metre" seavolume=1.4e9*1e9 #cubic km * 1e9 to convert to cubic metres print(paste(round(seavolume), "cubic metres, per levitus"))  "1.4e+18 cubic metres, per levitus" fractionto2000m=0.46 # fraction of ocean above 2000 m depth per Levitus zjoulesperdeg=seavolume*fractionto2000m*seadensity*heatcapacity/1e21 print(paste(round(zjoulesperdeg), "zetajoules to heat 2 km seawater by 1°C"))  "2631 zetajoules to heat 2 km seawater by 1°C" z1955error = 95 # 1955 error in ZJ print(paste(round(z1955error/zjoulesperdeg,2),"°C 1955 error"))  "0.04 °C 1955 error" z2018error = 9 # 1955 error in ZJ print(paste(round(z2018error/zjoulesperdeg,3),"°C 2018 error"))  "0.003 °C 2018 error" yr2018change = 8 # 2017 to 2018 change in ZJ print(paste(round(yr2018change/zjoulesperdeg,3),"°C change 2017 - 2018"))  "0.003 °C change 2017 - 2018"
via Watts Up With That? http://bit.ly/1Viafi3