Last active
July 1, 2024 13:57
-
-
Save dawnandrew100/e680c66fa20162f0a3ce85a0805f35ac to your computer and use it in GitHub Desktop.
A linearity calculator based on EP Evaluator.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.