Files
hpos-data/scripts/device_accuracy.py
Pritimay Sarkar 283de77d37 add r2 in plot
2024-02-10 20:24:43 +05:30

259 lines
10 KiB
Python

import pandas as pd
import numpy as np
import os
import re
import xlsxwriter
from datetime import datetime
import math
import matplotlib.pyplot as plt
import io
from scipy.stats import linregress
curdir = os.getcwd()
path_delim = '/'
precision_threshold = 0.009
accuracy_threshold = 0.02
denovix_reference_values = {
"Tartrazine": {
"10": 0.187191676,
"30": 0.187191676,
"50": 0.187447015,
"60": 0.187191676,
"75": 0.294739334,
"90": 0.187191676,
"100": 0.410113264,
"120": 0.187191676,
"125": 0.512526022,
"150": 0.622883833,
"175": 0.721095085,
"180": 0.187191676,
"200": 0.824065454,
"210": 0.187191676,
"225": 0.931385567,
"250": 1.002224884,
"275": 1.091996882,
"300": 1.16558307,
"350": 1.16558307,
},
"KMnO4": {
"10": 0.187191676,
"100": 0.187191676,
"145": 0.187191676,
"175": 0.187191676,
"250": 0.077846397,
"290": 0.359288533,
"350": 0.111884606,
"435": 0.187191676,
"450": 0.138031952,
"550": 0.175111257,
"580": 0.628039375,
"650": 0.208337398,
"720": 0.187191676,
"750": 0.238773222,
"850": 0.267574903,
"950": 0.299568449,
"1050": 0.329271864,
"1150": 0.365827844,
"1160": 0.388015579,
"1250": 0.388015579,
"1350": 0.417304075,
"1450": 0.447634285,
"1550": 0.478220085,
},
"HB": {
"2": 0.187191676,
"4": 0.187191676,
"5": 0.187191676,
"6": 0.187191676,
"8": 0.077846397,
},
}
df_reference_device = pd.DataFrame(denovix_reference_values).T
df_reference_device.index.name = 'Solution'
df_reference_device.columns.name = 'Wavelength'
df = pd.read_excel(curdir + path_delim + "data" + path_delim + "data_06_12_2023_12_47.xlsx", sheet_name="data")
if not df.empty:
conditions = [df['name'].str.contains('Tar', case=False, na=False),
df['name'].str.contains('KM', case=False, na=False),
df['name'].str.contains('HB', case=False, na=False)]
choices = ['Tartrazine', 'KMnO4', "HB"]
df['solution'] = np.select(conditions, choices, default=None)
def extract_numbers(s):
if "HB" in s:
match = re.search(r'-(\d+)$', s)
else:
match = re.search(r'-(\d+)', s) or re.search(r'(\d+)', s)
if match:
return int(match.group(1))
else:
return None
df["concentration"] = df['name'].apply(extract_numbers)
df['absorbance'] = df.apply(lambda row: row['led2Average'] if row['solution'] == 'Tartrazine' else row['led1Average'], axis=1)
df_precision_acc = df[["deviceId", "solution", "concentration", "led1Average", "led2Average", "absorbance"]].groupby(["deviceId", "solution", "concentration"]).describe()["absorbance"][["count", "min", "max", "mean"]]
df_precision_acc['precision'] = df_precision_acc['max'] - df_precision_acc['min']
df_precision_acc['precision_result'] = ['Fail' if diff > precision_threshold else 'Pass' for diff in df_precision_acc['precision']]
df_precision_acc.reset_index(inplace=True)
df_precision_acc.set_index(["deviceId", "solution", "concentration"], inplace=True)
device_precision_results = {}
for index, group_df in df_precision_acc.groupby(level=[0, 1, 2]):
soln = index[1]
concen = index[2]
result_values = group_df['precision_result'].values
if index[0] not in device_precision_results:
device_precision_results[index[0]] = "Pass"
else:
result = 'Fail' if 'Fail' in result_values else 'Pass'
if result == 'Fail':
device_precision_results[index[0]] = 'Fail'
df_device_precicion_results = pd.DataFrame(list(device_precision_results.items()), columns=['Device', 'Result'])
df_precision_acc = df_precision_acc.assign(accuracy='', accuracy_result='')
device_accuracy_results = {}
for idx, row in df_precision_acc.iterrows():
device_id = row.name[0]
solution = row.name[1]
concentration = str(int(row.name[2]))
# print(concentration)
try:
reference_value = denovix_reference_values[solution][concentration]
accuracy = math.fabs(row['mean'] - reference_value)
df_precision_acc.at[idx, 'accuracy'] = accuracy
accuracy_result = 'Pass' if accuracy < accuracy_threshold else 'Fail'
df_precision_acc.at[idx, 'accuracy_result'] = accuracy_result
if device_id not in device_accuracy_results:
device_accuracy_results[device_id] = accuracy_result
else:
if accuracy_result == 'Fail':
device_accuracy_results[device_id] = 'Fail'
except KeyError:
df_precision_acc.at[idx, 'accuracy'] = np.nan
df_precision_acc.at[idx, 'accuracy_result'] = 'Fail'
df_device_accuracy_results = pd.DataFrame(list(device_accuracy_results.items()), columns=['Device', 'Result'])
device_results = {}
df_device_results = pd.merge(df_device_precicion_results, df_device_accuracy_results, on='Device', how='outer', suffixes=('_precision', '_accuracy'))
df_device_results['Result'] = np.where((df_device_results['Result_precision'] == 'Fail') | (df_device_results['Result_accuracy'] == 'Fail'), 'Fail', 'Pass')
output_filename = f'data/accuracy_{datetime.today().strftime("%d_%m_%Y_%H_%M")}.xlsx'
writer = pd.ExcelWriter(output_filename, engine = 'xlsxwriter')
df_device_results.to_excel(writer, sheet_name="device_results")
df_precision_acc.to_excel(writer, sheet_name="concentration")
df.to_excel(writer, sheet_name="in")
df_reference_device.to_excel(writer, sheet_name="reference_device")
workbook = writer.book
worksheet_device_results = writer.sheets['device_results']
red_format = workbook.add_format({'bg_color': '#FFC7CE', 'font_color': '#9C0006'})
green_format = workbook.add_format({'bg_color': '#C6EFCE', 'font_color': '#006100'})
worksheet_device_results.conditional_format('E2:E{}'.format(df_device_results.shape[0] + 1), {'type': 'text',
'criteria': 'containing',
'value': 'Fail',
'format': red_format})
worksheet_device_results.conditional_format('E2:E{}'.format(df_device_results.shape[0] + 1), {'type': 'text',
'criteria': 'containing',
'value': 'Pass',
'format': green_format})
wks1 = workbook.add_worksheet('abs_plot')
wks1.write(0,0,'Abs Plot')
# Write the header
wks1.write(0, 0, 'Solution')
wks1.write(0, 1, 'Slope')
wks1.write(0, 2, 'Intercept')
wks1.write(0, 3, 'R^2')
# Create a new DataFrame to store linear fit results
df_linearfit_results = pd.DataFrame(columns=['Solution', 'Slope', 'Intercept', 'R^2'])
row = 1
fig, ax = plt.subplots()
filtered_df = df[df['solution'].isin(['KMnO4', 'Tartrazine', 'HB'])]
for solution in filtered_df['solution'].unique():
if pd.notna(solution):
solution_data = df[df['solution'] == solution]
concentration_means = solution_data.groupby('concentration')['absorbance'].mean()
# Fit a linear regression model
coefficients = np.polyfit(concentration_means.index, concentration_means.values, 1)
# Use linregress to get additional statistics including R-squared
slope, intercept, r_value, p_value, std_err = linregress(concentration_means.index, concentration_means.values)
print(f'For {solution}: Slope={slope:.4f}, Intercept={intercept:.4f}, R^2={r_value**2:.4f}')
ax.plot(concentration_means.index, concentration_means.values, marker='o', linestyle='-', label=f'Mean Absorbance for {solution}')
ax.plot(concentration_means.index, np.polyval(coefficients, concentration_means.index), linestyle='--', label=f'Linear Fit for {solution}')
annotation_text = f'Slope: {slope:.4f}\nIntercept: {intercept:.4f}\nR^2: {r_value**2:.4f}'
ax.text(concentration_means.index[-1] + 5, np.polyval(coefficients, concentration_means.index[-1]), annotation_text, fontsize=10, verticalalignment='center')
wks1.write(row, 0, solution)
wks1.write(row, 1, str(slope))
wks1.write(row, 2, str(intercept))
wks1.write(row, 3, str(r_value**2))
# Add the linear fit results to the new DataFrame
df_linearfit_results = df_linearfit_results.append({'Solution': solution, 'Slope': slope, 'Intercept': intercept, 'R^2': r_value**2}, ignore_index=True)
row += 1
ax.set_title('Mean Absorbance for Each Solution')
ax.set_xlabel('Concentration')
ax.set_ylabel('Mean Absorbance')
ax.legend()
# plt.show()
imgdata=io.BytesIO()
fig.savefig(imgdata, format='png')
wks1.insert_image(6,0, '', {'image_data': imgdata})
# Write linear fit results to Excel
df_linearfit_results.to_excel(writer, sheet_name="linearfit_results")
### debug each row
# # Merge df and df_device_precicion_results on deviceId
# df_merged = pd.merge(df, df_device_precicion_results, left_on='deviceId', right_on='Device', how='left')
# # Drop the duplicate "Device" column
# df_merged.drop(columns=['Device'], inplace=True)
# # Save the merged DataFrame to the Excel file
# df_merged.to_excel(writer, sheet_name="merged_results")
writer.close()
else:
empty_output_filename = f'data/empty_excel_{datetime.today().strftime("%d_%m_%Y_%H_%M")}.xlsx'
pd.DataFrame().to_excel(empty_output_filename, engine='xlsxwriter', index=False)