Skip to content

Instantly share code, notes, and snippets.

@dawnandrew100
Last active July 1, 2024 13:57
Show Gist options
  • Save dawnandrew100/e680c66fa20162f0a3ce85a0805f35ac to your computer and use it in GitHub Desktop.
Save dawnandrew100/e680c66fa20162f0a3ce85a0805f35ac to your computer and use it in GitHub Desktop.
A linearity calculator based on EP Evaluator.
import matplotlib.pyplot as plt
import numpy as np
dilutions = [1,2,4,8,10,16,20]
capi1_raw_data = [77.5,56.8,38.2,26.2,24.8,19.8,18.0,12.5]
capi2_raw_data = [77.2,53.3,37.4,26.2,23.8,20.6,18.6,13.8]
class linearity:
def __init__(self, raw, dilution):
self.raw = raw[:-1]
self.dilution = dilution
self.observed = []
self.target = []
self.base = raw[-1]
def __str__(self):
#line of best fit equation
_, m, b = self.linear_regression()
if b < 0:
return f"{m:.2f}x{b:.2f}"
return f"{m:.2f}x+{b:.2f}"
def __calculateObserved(self):
for value in self.raw:
if value == self.raw[0]:
self.observed.append(value)
continue
temp_value = value-self.base
self.observed.append(temp_value)
def __calculateTarget(self):
for value in self.dilution:
temp_value = self.raw[0]/value
self.target.append(temp_value)
def linear_regression(self):
if not self.observed: #only runs if list is empty
self.__calculateObserved()
self.__calculateTarget()
p = np.poly1d(np.polyfit(self.target, self.observed, 1))
a, b = np.polyfit(self.target, self.observed, 1)
return p, a, b
def average_distance(self, normalized=False):
if not self.observed: #only runs if list is empty
self.__calculateObserved()
self.__calculateTarget()
average, avg_norm = [], []
for i, num in enumerate(self.observed):
avg = (num+self.target[i])/2
sub = min(num, self.target[i])
average.append(avg-sub)
if normalized:
for num in average:
avg_norm.append((num-min(average))/(max(average)-min(average)))
average = avg_norm
return average
def plot(self):
if not self.observed: #only runs if list is empty
self.__calculateObserved()
self.__calculateTarget()
plt.figure()
_, ax = plt.subplots() #for formatting y=mx+b equation
p, a, b = self.linear_regression()
plt.xlabel("Measured")
plt.ylabel("Expected")
plt.scatter(self.target, self.observed)
plt.plot(self.target,p(self.target))
plt.text(.01, .99, f'y = {a:.2f}x + {b:.2f}', ha='left', va='top', size=14, transform=ax.transAxes)
def plot_dist(self, dist_eq):
plt.figure()
x = np.linspace(0,len(dist_eq), num=len(dist_eq))
plt.ylabel("Distance")
plt.plot(x,dist_eq)
def linearity_pass(self):
_, m, b = self.linear_regression()
if m > 0.95 and m < 1.05:
return True
return False
capi1Linearity = linearity(capi1_raw_data,dilutions)
capi2Linearity = linearity(capi2_raw_data,dilutions)
print(capi1Linearity.linearity_pass())
print(capi2Linearity.linearity_pass())
capi1Linearity.plot()
plt.show()
capi2Linearity.plot()
plt.show()
dist = capi1Linearity.average_distance(normalized=True)
capi1Linearity.plot_dist(dist)
plt.show()
dist = capi2Linearity.average_distance(normalized=False)
capi2Linearity.plot_dist(dist)
plt.show()
@dawnandrew100
Copy link
Author

This program not only plots linearity but also shows distance to an equidistant midpoint (with ability to see normalized distance). The "target" calculations are based on the 1 dilution representing a 1:1 dilution (aka neat dilution) of a monoclonal gamma region within a serum protein electrophoresis sample and the last number in the array representing the gamma region of a normal sample. The normal sample value is subtracted from each point in order to isolate the monoclonal spike percentage of the gamma portion of the serum protein electrophoresis. The purpose of this linearity is to prove that the machine accurately interprets differing gamma percentages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment