Standard scores#

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
import pandas as pd
pd.set_option('mode.copy_on_write', True)

Describing distributions#

We have seen several examples of distributions.

We can describe distributions as having a center, and a spread.

In the mean as predictor, we saw that the mean is a useful measure of the center of a distribution.

What measure should we use for the spread?

Back to chronic kidney disease#

We return to the data on chronic kidney disease.

Download the data to your computer via this link: ckd_clean.csv.

ckd_full = pd.read_csv('ckd_clean.csv')
ckd_full.head()
Age Blood Pressure Specific Gravity Albumin Sugar Red Blood Cells Pus Cell Pus Cell clumps Bacteria Blood Glucose Random ... Packed Cell Volume White Blood Cell Count Red Blood Cell Count Hypertension Diabetes Mellitus Coronary Artery Disease Appetite Pedal Edema Anemia Class
0 48.0 70.0 1.005 4.0 0.0 normal abnormal present notpresent 117.0 ... 32.0 6700.0 3.9 yes no no poor yes yes 1
1 53.0 90.0 1.020 2.0 0.0 abnormal abnormal present notpresent 70.0 ... 29.0 12100.0 3.7 yes yes no poor no yes 1
2 63.0 70.0 1.010 3.0 0.0 abnormal abnormal present notpresent 380.0 ... 32.0 4500.0 3.8 yes yes no poor yes no 1
3 68.0 80.0 1.010 3.0 2.0 normal abnormal present present 157.0 ... 16.0 11000.0 2.6 yes yes yes poor yes no 1
4 61.0 80.0 1.015 2.0 0.0 abnormal abnormal notpresent notpresent 173.0 ... 24.0 9200.0 3.2 yes yes yes poor yes yes 1

5 rows × 25 columns

We will use this dataset to get a couple of variables (columns) and therefore a couple of distributions.

Let’s start with the White Blood Cell Count, usually abbreviated as WBC.

wbc = ckd_full['White Blood Cell Count']
wbc.hist()
plt.title('White Blood Cell Count');
../_images/30002c2b0e92a0a134eab96179897c7b1b4a716b6911381644197363929c74d5.png
wbc.describe()
count      158.000000
mean      8475.949367
std       3126.880181
min       3800.000000
25%       6525.000000
50%       7800.000000
75%       9775.000000
max      26400.000000
Name: White Blood Cell Count, dtype: float64

Compare this to Hemoglobin concentrations:

hgb = ckd_full['Hemoglobin']
hgb.hist()
plt.title('Hemoglobin');
../_images/4a6b2937ee3d81ef70c0a4a5abed3ae250cc4433e016ad99f781fbcf74df8d03.png
hgb.describe()
count    158.000000
mean      13.687342
std        2.882204
min        3.100000
25%       12.600000
50%       14.250000
75%       15.775000
max       17.800000
Name: Hemoglobin, dtype: float64

Notice that we can’t easily plot these two on the same axes, because their units are so different.

Here’s what that looks like. Notice that the hemoglobin values disappear in a tiny spike to the left.

# Use alpha to make the histograms a little transparent.
# Label them for a legend.
hgb.hist(alpha=0.7, label='HGB')
wbc.hist(alpha=0.7, label='WBC')
plt.title("HGB and WBC together - HGB tiny spike at left")
plt.legend();
../_images/92d04a2698eb467f07c4b1e0030ef6f3bec8170615a559c092b04e782b43fd06.png

We could try and fix this by subtracting the mean, as a center value, so the values are now deviations from the mean.

wbc_deviations = wbc - np.mean(wbc)
wbc_deviations.hist()
plt.title('White Blood Cell Count deviations');
../_images/e2cfdb3a53824095d25689312a80281da353a10883c50b2ea4416e9c72bbba49.png
hgb_deviations = hgb - np.mean(hgb)
hgb_deviations.hist()
plt.title('Hemoglobin deviations');
../_images/82998b4c3b2637213352b1b2fe59ad0a52fb79e844874866d01e58d18e109773.png

The deviations each have a mean very very close to zero, and therefore, they have the same center:

np.mean(wbc_deviations), np.mean(hgb_deviations)
(-1.8420145858692217e-13, 7.195369476051647e-16)

We still cannot sensibly plot them on the same axes, because the WBC values have a very different spread. The WBC values completely dominate the x axis of the graph. We can’t reasonably compare the WBC deviations to the Hemoglobin deviations, because they have such different units.

hgb_deviations.hist(alpha=0.7, label='HGB')
wbc_deviations.hist(alpha=0.7, label='WBC')
plt.title("HGB and WBC deviations - you can't see HGB")
plt.legend();
../_images/f1a4cf2ce8f041bf96af898fdb65d02c5750426d3b83057e9138e8b90061936e.png

We would like a measure of the spread of the distribution, so we can set the two distributions to have the same spread.

The standard deviation#

In the mean as predictor section, we found that mean was the best value to use as a predictor, to minimize the sum of squared deviations.

Maybe we could get an idea of the typical squared deviation, as a measure of spread?

hgb_deviations[:10]
0   -2.487342
1   -4.187342
2   -2.887342
3   -8.087342
4   -5.987342
5   -3.887342
6   -1.187342
7   -3.687342
8   -3.187342
9   -3.887342
Name: Hemoglobin, dtype: float64
hgb_dev_sq = hgb_deviations ** 2
hgb_dev_sq[:10]
0     6.186869
1    17.533831
2     8.336743
3    65.405097
4    35.848261
5    15.111426
6     1.409780
7    13.596489
8    10.159148
9    15.111426
Name: Hemoglobin, dtype: float64
hgb_dev_sq.hist()
plt.title('HGB squared deviations')
Text(0.5, 1.0, 'HGB squared deviations')
../_images/d40676ec114bbe6cdd0c403167ff1d24c4b333f3885d1405ad7b8937fc417d96.png

The center, or typical value, of this distribution, could be the mean.

hgb_dev_sq_mean = np.mean(hgb_dev_sq)
hgb_dev_sq_mean
8.254523313571543

This is the mean squared deviation. This is also called the variance. Numpy has a function to calculate that in one shot:

# The mean squared deviation is the variance
np.var(hgb)
8.254523313571543

The mean squared deviation is a good indicator of the typical squared deviation. What should we use for some measure of the typical deviation?

We could take the square root of the mean squared deviation, like this:

np.sqrt(hgb_dev_sq_mean)
2.873068623192203

This is a measure of the spread of the distribution. It is a measure of the typical or average deviation.

It is also called the standard deviation.

np.std(hgb)
2.873068623192203

We can make our distribution have a standard center and a standard spread by dividing our mean-centered distribution, by the standard deviation. Then the distribution will have a standard deviation very close to 1.

This version of the distribution, with mean 0 and standard deviation of 1, is called the standardized distribution.

standardized_hgb = hgb_deviations / np.std(hgb)
standardized_hgb.hist()
plt.title('Standardized Hemoglobin')
Text(0.5, 1.0, 'Standardized Hemoglobin')
../_images/f7b2908964ed0a05d93bf15335c00544c2a2e474dba627eaaa9b5e42355d4631.png

We can make a function to do this:

def standard_units(x):
    return (x - np.mean(x))/np.std(x)
std_hgb_again = standard_units(hgb)
std_hgb_again.hist()
plt.title('Standardized Hemoglobin, again')
Text(0.5, 1.0, 'Standardized Hemoglobin, again')
../_images/fab8cde2e9c8af2ad4ba367f4b3ea92368fbd51c8abc07cbb3217ea79a208a56.png

If we do the same to the WBC, we can compare values of the distributions:

std_wbc = standard_units(wbc)
std_wbc.hist()
plt.title('Standardized White Blood Cell Count')
Text(0.5, 1.0, 'Standardized White Blood Cell Count')
../_images/69773cefbc5c24e745be7dd827b6542d4725547ce89f7f4184c6a49aa7baf993.png

Now we can put both these distributions on the same graph, to compare them directly.

std_hgb_again.hist(alpha=0.7, label='HGB')
std_wbc.hist(alpha=0.7, label='WBC')
plt.title('Standardized HGB and WBC')
plt.legend()
<matplotlib.legend.Legend at 0x7fda49a6ae50>
../_images/0e2a83874e3c508cb49e21edf07d75da46bffa31a518470b83f3855cb3c0c57a.png

Every value in standardized units gives the deviation of the original value from its mean, in terms of the number of standard deviations.