codexproficiency-item-heterogeneity-and-averaging.py
proficiency-item-heterogeneity-and-averaging.py
"""Original duplicate-unit readings and the same-unit averaging variance floor."""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
def main():
readings = np.array([[99.4,99.6],[100.0,100.2],[99.8,100.0],[100.6,100.8]])
m,q = readings.shape
means,grand = readings.mean(axis=1),readings.mean()
msw = np.sum((readings-means[:,None])**2)/(m*(q-1))
msb = q*np.sum((means-grand)**2)/(m-1)
between = max(0.,(msb-msw)/q)
assert np.allclose([grand,msw,msb,between],[100.05,.02,.5,.24])
# Independently check the leave-one-out covariance derivation in the article.
n,sigma = 4,2.
transform = (n*np.eye(n)-np.ones((n,n)))/(n-1)
covariance = sigma**2*[email protected]
assert np.allclose(np.diag(covariance), n*sigma**2/(n-1))
assert np.allclose(covariance[~np.eye(n,dtype=bool)],-n*sigma**2/(n-1)**2)
assert np.allclose([email protected](n),0.)
repeats = np.arange(1,21)
scale = .2
total = np.sqrt(between+msw/repeats)/scale
analytical_only = np.sqrt(msw/repeats)/scale
floor = np.sqrt(between)/scale
fig,(left,right) = plt.subplots(1,2,figsize=(10,4.1),dpi=100,facecolor='white')
fig.subplots_adjust(left=.065,right=.98,bottom=.20,top=.76,wspace=.34)
for j,pair in enumerate(readings):
left.plot(pair,[j+1,j+1],color='#7e8d9b',lw=1.6)
left.scatter(pair,[j+1,j+1],s=38,color='#196b93',zorder=3)
left.scatter(means,np.arange(1,m+1),marker='|',s=180,color='black',label='Unit mean',zorder=4)
left.axvline(grand,color='#a44e29',ls='--',lw=1.4,label='Grand mean: 100.05')
left.set(xlim=(99.25,100.9),ylim=(.5,4.5),yticks=[1,2,3,4],xlabel='Concentration (mg/L)',ylabel='Supplied unit')
left.set_title('Two test portions from each unit',fontsize=11)
left.legend(fontsize=8,loc='lower right',framealpha=1)
right.plot(repeats,total,color='#196b93',lw=2.3,label='Item + analytical variation')
right.axhline(floor,color='#a44e29',ls='--',lw=1.5,label=f'Item floor: {floor:.2f}')
right.plot(repeats,analytical_only,color='#43894a',lw=1.8,label='Analytical variation alone')
right.set(xlim=(1,20),ylim=(0,2.85),xticks=[1,5,10,15,20],xlabel='Analyses averaged from the same unit',ylabel='Score standard deviation')
right.set_title('Assessment scale: 0.20 mg/L',fontsize=11)
right.legend(fontsize=8,loc='center right',framealpha=1)
for ax in [left,right]:ax.grid(alpha=.20)
fig.suptitle('Item heterogeneity persists under repeated analysis',fontsize=14,y=.97)
fig.text(.5,.84,'Original illustrative data; between-unit variance 0.24 and within-unit variance 0.02 (mg/L)²',ha='center',fontsize=10)
fig.text(.5,.035,'Conditional model: independent analytical repeats share one unchanged unit effect.',ha='center',fontsize=10)
fig.savefig(Path.cwd()/'proficiency-item-heterogeneity-and-averaging.png',dpi=100,facecolor='white')
plt.close(fig)
if __name__=='__main__':
main()