dashboards / app.py
drkvcsstvn's picture
Update app.py
6339766 verified
import pandas as pd
import numpy as np
import panel as pn
import scipy.stats as stats
pn.extension('tabulator')
import hvplot.pandas
import holoviews as hv
from bokeh.models import HoverTool
from bokeh.models import GlyphRenderer, LinearAxis, LinearScale, Range1d
import os
from natsort import natsort_keygen
import threading
import time
import schedule
from datetime import datetime
from huggingface_hub import HfApi
# Initialize HfApi
#repo_id = "pilotakekszel/dashboards_v0.1_retro"
#api_token = os.getenv("HF_API_TOKEN")
#if not api_token:
# raise ValueError("API token not found! Ensure HF_API_TOKEN is set.")
#api = HfApi(token=api_token)
def restart_space():
response = api.restart_space(repo_id=repo_id)
def schedule_restart():
# Schedule the restart every minute
# FOR TESTING:
#schedule.every(1).minutes.do(restart_space)
schedule.every().day.at("06:00").do(restart_space)
while True:
schedule.run_pending()
time.sleep(1)
def start_scheduler():
# Start the scheduler in a separate thread
scheduler_thread = threading.Thread(target=schedule_restart, daemon=True)
scheduler_thread.start()
# In[2]:
if 'data' not in pn.state.cache.keys():
df = pd.read_csv('kgyc1.csv')
df = df.rename(columns={'ASC-US': 'ASC-US/ASC-H ratio(%)'})
#df['Referral Rate(%)'] = df['Referral Rate(%)']*100
df['ASC-US/ASC-H ratio(%)'] = df['ASC-US/ASC-H ratio(%)']*100
df['ASC-H'] = df['ASC-H']*100
df = df.rename(columns={'index': 'people'})
df = df.rename(columns={'HPV/ASC-US': 'hrHPV/ASC-US rate (%)'})
df = df.rename(columns={'HPV/population': 'hrHPV Prevalence (%)'})
#df.at[0, 'Number'] = 151
df.iat[0, 5] = 2.25
df = df[~((df['people'].str.contains('ytolog')) & (df['Number'] < 1000))]
df = df[~((df['people'].str.contains('path')) & (df['Number'] < 200))]
#df = df[df['year'] != 2017]
# Invalid rows - not representative for other reasons:
#df = df[df['people'] != 'Cytotechnologist 21']
#df = df.reset_index(drop=True)
#df = df.drop(19)
# Replace 'cytologist' with 'cytotechnologist' in the index
df['people'] = df['people'].str.replace('Cytologist', 'Cytotechnologist', regex=False)
pn.state.cache['data'] = df.copy()
else:
df = pn.state.cache['data']
# In[3]:
sdot_size = 500
sdot_person = 1500
sheight = 800
f_scale = 2
f_scale_lab = 1.5
fpx = '28px'
legend_px = '24px'
pl_title = {'title': 14}
stylesheet = f"""
:host {{
--handle-width: 30px;
--handle-height: 50px;
--slider-size: 25px;
}}
"""
select_style = """
.bk-input {
font-size: 20px;
}
"""
mobile_style = {'font-size': f'{fpx}', 'padding': '40px'}
first_year = df['year'].nsmallest(2).iloc[-1]
col_options = ['ASC/LSIL', 'ASC-US/ASC-H ratio(%)','Abnormal Rate(%)','Referral Rate(%)', 'hrHPV/ASC-US rate (%)', 'hrHPV Prevalence (%)']
column_changer = pn.widgets.Select(name='Select QA Parameter', options=col_options, styles = mobile_style, width = 400, height=150, margin = 0,stylesheets= [select_style])
year_slider = pn.widgets.Select(name='Select Year', value = int(df.year.max()),options=list(range(int(first_year), int(df.year.max())+1)), styles = mobile_style, width = 400, height=150, margin = 0,stylesheets= [select_style])
# In[4]:
modal = f"""
<div><br><br><p style = "text-align: justify;">
<span style="font-size: {fpx}; font-weight: bold">This application's development is in progress. For the more fluent user experience use a tablet or laptop. </span><br><br>
<span style="font-size: {fpx}; font-weight: bold">TABS: </span><br><br>
<span style="font-size: {fpx};">1. With the first level of tabs, outcomes are presented in three formats for the </span>\
<span style="font-size: {fpx}; font-weight: bold">Laboratory management</span>
<span style="font-size: {fpx};">for the </span>
<span style="font-size: {fpx}; font-weight: bold">Cytopathologists</span>
<span style="font-size: {fpx};">and for the </span>
<span style="font-size: {fpx}; font-weight: bold">Cytologists</span>
<span style="font-size: {fpx};">. You can choose between them by clicking on the tabs.
</span><br>
<span style="font-size: {fpx};">2. You can toggle between </span>
<span style="font-size: {fpx}; font-weight: bold">Performance Results</span>
<span style="font-size: {fpx};">, and</span>
<span style="font-size: {fpx}; font-weight: bold">Results Overviews</span>
<span style="font-size: {fpx};">with the second level tabs. </span><br>
<span style="font-size: {fpx};">3. Use the Year Slider to choose the year of your interest. </span><br>
<span style="font-size: {fpx};">4. Choose the QA parameter. </span>
<br><br><br>\
<span style="font-size: {fpx}; font-weight: bold">GRAPHS: </span><br><br>\
<span style="font-size: {fpx}; font-weight: bold">QA Parameter graphs: </span>
<span style="font-size: {fpx};">Results are shown in comparison with </span>
<span style="font-size: {fpx}; font-weight: bold; background-color: #ACFFA0" >laboratory target </span>
<span style="font-size: {fpx}" >,</span>
<span style="font-size: {fpx}; font-weight: bold; background-color: #6AB35F" >borderline </span>
<span style="font-size: {fpx}" >and</span>
<span style="font-size: {fpx}; font-weight: bold; background-color: #B34D93" >attention zones </span>
<span style="font-size: {fpx}">that were defined by standard deviations \
and absolute percentage values from reference values on professionally established benchmarks.
The dashed line represent the </span>
<span style="font-size: {fpx}; font-weight: bold; color: #5B91D1">laboratory result</span>
<span style="font-size: {fpx}">, while the straight black line represents the </span>
<span style="font-size: {fpx}; font-weight: bold">international reference</span>
<span style="font-size: {fpx}">.<br>
By hitting the dots a tooltip will appear.</span><br><br>
<span style="font-size: {fpx}; font-weight: bold">Reference Curve vs Laboratory Histogram: </span>
<span style="font-size: {fpx};">Without data transformation, results showed no normal distribution (Shapiro-Wilk test p < 0.05).
In case of </span>
<span style="font-size: {fpx};font-weight: bold;">ASC/LSIL ratio, ASC-US/ASC-H ratio (%) and Abnormal ratio, </span>
<span style="font-size: {fpx};font-weight: bold;">reference normal distribution curves</span>
<span style="font-size: {fpx};">were compared with </span>
<span style="font-size: {fpx};font-weight: bold; color: #5B91D1">histograms of laboratory results</span>
<span style="font-size: {fpx};">and </span>
<span style="font-size: {fpx};font-weight: bold; color: #FE0F17">Z scores </span>
<span style="font-size: {fpx};">of outliers were highlighted on the graph.</span><br><br><br>
<span style="font-size: {fpx}; font-weight: bold">RESULTS: </span><br><br>\
<span style="font-size: {fpx}; font-weight: bold">Results Overviews</span>
<span style="font-size: {fpx};">interpret graphs for the user, highlighting outliers regarding the chosen QA parameter,
and tendencies in performance.
</span><br><br></p>
</div>
"""
# Create a button
modal_style = """
.bk-btn.bk-btn-default {
font-size: 24px;
font-weight: bold;
}
"""
modal_btn = pn.widgets.Button(name="Click for hints to use", stylesheets = [modal_style])
# Callback that will open the modal when the button is clicked
def about_callback(event):
template.open_modal()
# Link the button to the callback and append it to the sidebar
modal_btn.on_click(about_callback)
legend_df_indiv = ['Reference', 'Lab result','Lab goal','Borderline', 'Attention']
# In[5]:
legend_df_indiv = pd.DataFrame({'Legend': legend_df_indiv})
# In[6]:
vlegend_df_indiv = legend_df_indiv.style
# In[7]:
#COLORS (EGGPLANT): ['#ACFFA0','#6AB35F','#B34D93']
vlegend_df_indiv.set_table_styles([ # create internal CSS classes
{'selector': '.ref', 'props': f'border: 2px solid #242124; color:black; font-weight: bold; font-size: {legend_px}'},
{'selector': '.lab', 'props': f'border: 2px dashed #5B91D1; color:#5B91D1; font-weight: bold; font-size: {legend_px}' },
{'selector': '.goal', 'props': f'background-color: #ACFFA0;; font-weight: bold; font-size: {legend_px}' },
{'selector': '.att', 'props': f'background-color: #B34D93;; font-weight: bold; font-size: {legend_px}' },
{'selector': '.bdln', 'props': f'background-color: #6AB35F;; font-weight: bold; font-size: {legend_px}' }
], overwrite=False)
cell_color = pd.DataFrame([['ref'],['lab'],['goal'],['bdln'],['att']],
index=vlegend_df_indiv.index,
columns=vlegend_df_indiv.columns[:4])
vertical_legend_indiv=vlegend_df_indiv.set_td_classes(cell_color)
vertical_legend_indiv =vertical_legend_indiv.hide(axis='columns')
vertical_legend_indiv =vertical_legend_indiv.hide(axis='rows')
vertical_legend_indiv= pn.pane.DataFrame(vertical_legend_indiv, index= False)
vertical_legend_indiv= pn.Column("## Legend", vertical_legend_indiv)
zcol = [col_options[0],col_options[1],col_options[2]]
zscore_base = df[zcol]
new_row = [0.75, 12.5, 1.5]
zscore_base.iloc[1] = new_row
zscore_df = (zscore_base.iloc[2:] - zscore_base.iloc[0]) /zscore_base.iloc[1]
zscore_df['year'] = df['year']
zscore_df['people'] = df['people']
zscore_eval = zscore_df.groupby(['people'])
# In[8]:
# LAB OVERVIEW
def find_first_decrease_in_performance(lst):
x_values = []
for i in range(len(lst) - 1):
if lst[i] + 1 == lst[i + 1]:
x_values.append(lst[i])
else:
x_values = [lst[-1]]
return x_values[0]
def find_values_less_than_previous(input_list):
less_than_previous_indexes = []
for i in range(1, len(input_list)):
if (input_list[i] < input_list[i - 1]):
less_than_previous_indexes.append(i-1)
return less_than_previous_indexes
def find_values_more_than_previous(input_list):
more_than_previous_indexes = []
for i in range(1, len(input_list)):
if (input_list[i] > input_list[i - 1]):
more_than_previous_indexes.append(i-1)
return more_than_previous_indexes
def find_last_decreasing_element(lst):
for i in range(1, len(lst)):
if lst[i] != lst[i - 1] - 1:
return lst[i-1]
return lst[len(lst)-1]
def labres(column_changer):
lab_zscore = zscore_eval.get_group('KGYC')
abs_results = df[df['people'] == 'KGYC']
lab_comment_5 = ""
asc_abn_outliers = zscore_df[(zscore_df['people'].str.contains('Cyto'))&(zscore_df['year']==int(df.year.max()))]
asc_abn_outliers_filtered = asc_abn_outliers[~asc_abn_outliers['people'].str.contains('Cytotechnologist 8', case=False, na=False)]
ref_outlier = df[(df['people'].str.contains('Cytot')) & (df['year'] == int(df.year.max()))]
ref_outlier_filtered = ref_outlier[~ref_outlier['people'].str.contains('Cytotechnologist 8', case=False, na=False)]
#thy_outlier = df[(df['people'].str.contains('Cytop')) & (df['year'] == int(df.year.max()))]
# ASCUS & ABNORMAL:
lab_comment_3= f"No outliers regarding {column_changer}"
lab_comment_4 = ""
if column_changer == col_options[1]:
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)] #lab_ascus_reference - 3 * sd_dev így lenne helyesebb, de az attention zone valójában már a borderline alsó értékétől számítandó
descript = (f"**Description:** *ASC-US/ASC-H ratio literature reference value 90% / 10%. "
f"Given our average laboratory Z score of {avg_zscore:.2f}, the laboratory's average ratio is "
f"{lab_ascus_reference:.2f}% / {lab_asch_reference:.2f}%.* <br><br> "
f"Given our average laboratory Z score, the goal, borderline, and attention zones calculated with 95% CI are the following: "
f"**Goal Zone:** {goal_zone[0]:.2f} to {goal_zone[1]:.2f}, "
f"**Borderline Zone:** {borderline_zone[0][0]:.2f} to {borderline_zone[0][1]:.2f} "
f"and {borderline_zone[1][0]:.2f} to {borderline_zone[1][1]:.2f}, "
f"**Attention Zone:** {attention_zone[0][0]:.2f} to {attention_zone[0][1]:.2f} "
f"and {attention_zone[1][0]:.2f} to {attention_zone[1][1]:.2f}. <br><br>")
if column_changer == col_options[2]:
descript = "**Description:** *Abnormal Rate represents the relative ratio of ASC, SIL and AGC diagnoses. Alterations ( Z Score: <-2) means a level of insecurity that requires attention.*<br><br>"
if column_changer == col_options[1] or column_changer == col_options[2]:
lab_value = lab_zscore[column_changer].iloc[len(lab_zscore)-1]
if lab_value >= -1:
lab_comment = f"Latest record of laboratory average value is within one standard deviation from reference average (Z score: {lab_value:.2f}), "
elif -1 > lab_value > -2:
lab_comment = f"Latest record of laboratory average value is in the borderline zone regarding performance compared to reference (Z score: {lab_value:.2f}), "
else:
lab_comment = f"Attention needed!, laboratory performance is considerably deviant from reference value, global laboratory practice revision is advised. (Z score {lab_value:.2f})"
if (-1 > lab_value) & (lab_zscore[column_changer].iloc[len(lab_zscore)-1] < lab_zscore[column_changer].iloc[len(lab_zscore)-2]):
decreasing_tendency = find_values_less_than_previous(lab_zscore[column_changer].to_list())
lab_comment_2 = f"with tendencious decrease in performance since year {decreasing_tendency[0]+int(first_year)}."
else:
lab_comment_2 = "with no relevant tendency or no tendency at all in decrease of performance. "
if (asc_abn_outliers_filtered[column_changer] < -2).any():
asc_abn_outly = asc_abn_outliers_filtered[asc_abn_outliers_filtered[column_changer] < -2]['people'].to_list()
lab_comment_3 = f"Outliers regarding {column_changer}: **{asc_abn_outly}**,"
lab_comment_4 = []
for o in asc_abn_outly:
if (len(zscore_eval.get_group(o))>1) & (zscore_eval.get_group(o)[column_changer].iloc[len(zscore_eval.get_group(o))-1] <= zscore_eval.get_group(o)[column_changer].iloc[len(zscore_eval.get_group(o))-2]):
first_out = find_values_less_than_previous(zscore_eval.get_group(o)[column_changer].to_list())
if len(first_out) > 2:
lab_comment_4.append(f"**{o}** with tendencious decrease since **{find_first_decrease_in_performance(first_out)+int(first_year)}**")
else:
lab_comment_4.append(f"**{o}** with tendencious decrease compared to last registered result in **{zscore_eval.get_group(o)['year'].iloc[0]}**")
lab_comment_5= ""
lab_comment_6= ""
# ASCLSIL:
elif column_changer == col_options[0]:
descript = f"**Description:** *ASC/LSIL value represents the ASC-US, ASC-H diagnoses compared to LSIL diagnoses. Considerable elevation (Z Score > 2) from reference value {df.iat[0,5]} requires attention. A lower result than 1.5 could imply an increase in false negative interpretations.*<br><br>"
lab_value = lab_zscore[column_changer].iloc[len(lab_zscore)-1]
if -1 < lab_value < 1:
lab_comment = f"Latest record of laboratory average value is within one standard deviation from reference average (Z score: {lab_value:.2f}), "
elif 1 < lab_value < 2:
lab_comment = f"Latest record of laboratory average value is in the borderline zone regarding performance compared to reference (Z score: {lab_value:.2f}), "
elif lab_value > 2:
lab_comment = f"Laboratory performance is considerably deviant from reference value, global laboratory practice revision is advised. (Z score {lab_value:.2f})"
elif lab_value < -1:
lab_comment = f"Laboratory result is below one standard deviation from reference average (Z score: {lab_value:.2f}), indicating a risk of low sensitivity."
if (lab_value > 1) & (lab_zscore[column_changer].iloc[len(lab_zscore)-1] > lab_zscore[column_changer].iloc[len(lab_zscore)-2]):
increasing_tendency = find_values_more_than_previous(lab_zscore[column_changer].to_list())
lab_comment_2 = f"with tendencious decrease in performance since year {increasing_tendency[0]+int(first_year)}."
else:
lab_comment_2 = ""
if (asc_abn_outliers_filtered[column_changer] > 2).any():
asclsil_outly = asc_abn_outliers_filtered[asc_abn_outliers_filtered[column_changer] > 2]['people'].to_list()
lab_comment_3 = f"Outliers regarding {column_changer}: **{asclsil_outly}**,"
lab_comment_4 = []
for o in asclsil_outly:
if (len(zscore_eval.get_group(o))>1) & (zscore_eval.get_group(o)[column_changer].iloc[len(zscore_eval.get_group(o))-1] >= zscore_eval.get_group(o)[column_changer].iloc[len(zscore_eval.get_group(o))-2]):
first_out = find_values_more_than_previous(zscore_eval.get_group(o)[column_changer].to_list())
if len(first_out) > 2:
lab_comment_4.append(f"**{o}** with tendencious decrease since **{find_first_decrease_in_performance(first_out)+int(first_year)}**")
else:
lab_comment_4.append(f"**{o}** with tendencious decrease compared to last registered result in**{zscore_eval.get_group(o)['year'].iloc[0]}**")
lab_comment_5= ""
lab_comment_6= ""
# Thyroid Rate(%):
#elif column_changer == col_options[2]:
#descript = "**Description:** *Thyroid AUS Rate signifies the ratio of AUS (TBS 3) cytology reports. More than 15% requires attention.* <br><br>"
#thyroid_value = abs_results[column_changer].iloc[len(abs_results)-1]
#if thyroid_value < 10:
# lab_comment = f"Latest record of laboratory result is within laboratory goal zone. {thyroid_value:.2f}%"
#elif 10 < thyroid_value < 15:
# lab_comment = f"Latest record of laboratory result is in laboratory borderline zone. {thyroid_value:.2f}%"
#else:
# lab_comment = f"Latest laboratory result value is {thyroid_value:.2f}%, performance follow up and global laboratory practice revision is advised."
#if (thyroid_value > 10) & (abs_results[column_changer].iloc[len(abs_results)-1] > abs_results[column_changer].iloc[len(abs_results)-2]):
# increasing_tendency = find_values_more_than_previous(lab_zscore[column_changer].to_list())
# lab_comment_2 = f"with tendencious decrease in performance since year {increasing_tendency[0]+int(first_year)}."
#else:
# lab_comment_2 = "with no relevant tendency or no tendency at all in decrease of performance. "
#if (thy_outlier[column_changer] > 15).any():
# thy_outly = thy_outlier[thy_outlier[column_changer] > 15]['people'].to_list()
# lab_comment_3 = f"Outliers regarding {column_changer}: **{thy_outly}**,"
# for o in thy_outly:
# if (len(df.groupby(['people']).get_group(o))>1) & (df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-1] >= df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-2]):
# first_out = find_values_more_than_previous(df.groupby(['people']).get_group(o)[column_changer].to_list())
# if len(first_out) > 2:
# lab_comment_4.append(f"**{o}** with tendencious decrease since **{find_first_decrease_in_performance(first_out)+int(first_year)}**")
# else:
# lab_comment_4.append(f"**{o}** with tendencious decrease compared to last registered result in**{zscore_eval.get_group(o)['year'].iloc[first_out[0]]}**")
#lab_comment_5= ""
#lab_comment_6= ""
# Referral Rate(%)
elif column_changer == col_options[3]:
descript = "**Description:** *Referral Rate signifies the ratio the Cytotechnologist consults cases. < 5 % and > 15% requires attention.*<br><br>"
referral_value = abs_results['Referral Rate(%)'].iloc[len(abs_results)-1]
if 7 < referral_value < 13:
lab_comment = f"Latest record of laboratory result is within laboratory goal zone {referral_value:.2f}%. "
lab_comment_2 =""
elif 5 < referral_value < 7:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone. {referral_value:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] < abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"An underreferral tendency can be observed since last year."
else:
lab_comment_2 =""
elif 13 < referral_value < 15:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone {referral_value:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] > abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"An overreferral tendency can be observed since last year."
else:
lab_comment_2 =""
elif (referral_value < 5) or (referral_value > 15):
lab_comment = f"Latest laboratory result value is {referral_value:.2f}%, performance follow up and global laboratory practice revision is advised."
lab_comment_2 = ""
if (ref_outlier_filtered[column_changer] < 5).any():
ref_outly_down = ref_outlier_filtered[(ref_outlier_filtered[column_changer] < 5)]['people'].to_list()
lab_comment_3 = f"<br>Underreferral in case of **{ref_outly_down}**. "
lab_comment_4 = []
for o in ref_outly_down:
if (len(df.groupby(['people']).get_group(o))>1) & (df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-1] <= df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-2]):
first_out = find_values_less_than_previous(df.groupby(['people']).get_group(o)[column_changer].to_list())
if len(first_out) > 2:
lab_comment_4.append(f"**{o}** with tendencious decrease since **{find_first_decrease_in_performance(first_out)+int(first_year)}**")
else:
lab_comment_4.append(f"**{o}** with tendencious decrease compared to last registered result in **{df.groupby(['people']).get_group(o)['year'].iloc[first_out[0]]}**")
if (ref_outlier_filtered[column_changer] > 15).any():
ref_outly_up = ref_outlier_filtered[(ref_outlier_filtered[column_changer] > 15)]['people'].to_list()
lab_comment_5 = f"<br>Overreferral in case of **{ref_outly_up}**. "
lab_comment_6 = []
for o in ref_outly_up:
if (len(df.groupby(['people']).get_group(o))>1) & (df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-1] >= df.groupby(['people']).get_group(o)[column_changer].iloc[len(df.groupby(['people']).get_group(o))-2]):
first_out = find_values_more_than_previous(df.groupby(['people']).get_group(o)[column_changer].to_list())
if len(first_out) > 2:
lab_comment_6.append(f"**{o}** with tendencious decrease since **{find_first_decrease_in_performance(first_out)+int(first_year)}**")
else:
lab_comment_6.append(f"**{o}** with tendencious decrease compared to last registered result in**{zscore_eval.get_group(o)['year'].iloc[first_out[0]]}**")
#hrHPV/ASC-US ratio (%)
elif column_changer == col_options[4]:
abs_results = df[df['people'] == 'KGYC']
hrhpv_ascus = abs_results['hrHPV/ASC-US rate (%)'].iloc[len(abs_results)-1]
descript = "**Description:** *The hrHPV/ASC-US rate represents the high-risk HPV positive ASC-US cases among all ASC-US diagnoses. The literature reference value is 36.6%. A rate < 30% and > 60% could indicate problems with laboratory protocol or changes in population risk factors.*<br><br>"
if 30 < hrhpv_ascus < 60:
lab_comment = f"Latest record of laboratory result is within laboratory goal zone {hrhpv_ascus:.2f}%. "
lab_comment_2 =""
elif 20 < hrhpv_ascus < 30:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone. {hrhpv_ascus:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] < abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"A decreasing trend can be observed since last year, a lower than 30% rate could indicate problems with laboratory protocol or changes in population risk factors."
else:
lab_comment_2 =""
elif 60 < hrhpv_ascus < 70:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone {hrhpv_ascus:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] > abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"An increasing trend can be observed since last year, a higher than 60% rate could indicate problems with laboratory protocol or changes in population risk factors."
else:
lab_comment_2 =""
elif (hrhpv_ascus < 20) or (hrhpv_ascus > 70):
lab_comment = f"Latest laboratory result value is {hrhpv_ascus:.2f}%, performance follow up and global laboratory practice revision is advised."
lab_comment_2 = ""
#HPV Prevalence (%)
elif column_changer == col_options[5]:
abs_results = df[df['people'] == 'KGYC']
hrhpv_pop = abs_results['hrHPV Prevalence (%)'].iloc[len(abs_results)-1]
descript = "**Description:** *The hrHPV Prevalence represents the high-risk HPV prevalence rate among the laboratory's population. The literature reference value varies between 11% and 29% based on the region and type of population screened (Eastern Europe - 21%). A rate < 10% and > 30% could indicate problems with laboratory protocol or changes in population risk factors.*<br><br>"
if 10 < hrhpv_pop < 30:
lab_comment = f"Latest record of laboratory result is within laboratory goal zone {hrhpv_pop:.2f}%. "
lab_comment_2 =""
elif 5 < hrhpv_ascus < 10:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone. {hrhpv_pop:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] < abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"A decreasing trend can be observed since last year, a lower than 10% rate could indicate problems with laboratory protocol or changes in population risk factors."
else:
lab_comment_2 =""
elif 30 < hrhpv_ascus < 35:
lab_comment = f"Latest record of laboratory result is in laboratory borderline zone {hrhpv_pop:.2f}%."
if (abs_results[column_changer].iloc[len(abs_results)-1] > abs_results[column_changer].iloc[len(abs_results)-2]):
lab_comment_2 = f"An increasing trend can be observed since last year, a higher than 30% rate could indicate problems with laboratory protocol or changes in population risk factors."
else:
lab_comment_2 =""
elif (hrhpv_pop < 5) or (hrhpv_pop > 35):
lab_comment = f"Latest laboratory result value is {hrhpv_pop:.2f}%, performance follow up and global laboratory practice revision is advised."
lab_comment_2 = ""
lab_comment_6 = lab_comment_6 if 'lab_comment_6' in locals() else []
return pn.pane.Markdown(f""" ## {column_changer}:
{descript} {lab_comment} {lab_comment_2}
{lab_comment_3} {lab_comment_4} {lab_comment_5} {lab_comment_6}
""", sizing_mode ='stretch_both', styles = {'font-size':fpx})
inter_labres = pn.bind(labres, column_changer)
def lab_management(year_slider, column_changer):
line_data = df[df['people'].str.contains('KGYC')].sort_values(by="year").reset_index(drop=True)
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore #Végül 95%-os CI-vel alkottam meg a bdl és att zónákat
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)]
scatterdata = df[(df.year == year_slider)&(df.people.str.contains('KGYC|Cyto'))].sort_values(by="people", \
ascending=False,key=natsort_keygen()).reset_index(drop=True)
scatterdata_filtered = scatterdata[~scatterdata.people.str.contains('Cytotechnologist 8', case=False, na=False)]
ref = df[column_changer].iloc[0]
lab = scatterdata_filtered[column_changer].iloc[0]
max_x = scatterdata_filtered[column_changer].max()
max_x_abn = scatterdata_filtered[scatterdata_filtered.people.str.contains('ytolog')][column_changer].max()
min_x = scatterdata_filtered[column_changer].min()
if column_changer != 'ASC-US/ASC-H ratio(%)':
tooltips_cp = f"""
<div>
<span style="font-size: {fpx}; font-weight: bold">@people
</span><br>
<span style= "font-size:{fpx}; font-weight: bold; color: #5B91D1">{column_changer}:
</span>
<span style="font-size: {fpx};">@{{{column_changer}}}
</span>
</div>
"""
else:
tooltips_cp = f"""
<div>
<span style="font-size: {fpx}; font-weight: bold">@people
</span><br>
<span style= "font-size:{fpx}; font-weight: bold; color: #5B91D1">ASC-US(%):
</span>
<span style="font-size: {fpx};">@{{{column_changer}}}
</span>
</div>
"""
hover_cp = HoverTool(tooltips=tooltips_cp)
scatter_plot = scatterdata_filtered[scatterdata_filtered.people.str.contains('Cyto', na=False)].hvplot.scatter(x=column_changer, y='people', color='black', size=sdot_size, height= sheight,
xlim=(0,max_x+0.5), xticks = [0,lab,ref, max_x], grid=True, title=column_changer,
xformatter='%.1f',xlabel ='', ylabel='', tools = [hover_cp]).opts(fontsize = pl_title, fontscale=f_scale_lab, shared_axes=False, toolbar=None, default_tools = [])
ref_vline = hv.VLine(x=ref).opts(color='black', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
lab_vline = hv.VLine(x=lab).opts(color='#5B91D1', line_dash ='dashed',alpha=1).opts(shared_axes=False,toolbar=None, default_tools = [])
if column_changer == 'ASC/LSIL':
asc_lsil_goal = hv.VSpan(1.5, 3).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_1 = hv.VSpan(3, 3.75).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_2 = hv.VSpan(0, 1.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_att = hv.VSpan(3.75, 15).opts(shared_axes=False,toolbar=None, default_tools = [])
asclsil_bg =(asc_lsil_goal.opts(color='#ACFFA0', alpha=0.75) * asc_lsil_bdln_2.opts(color='#6AB35F', alpha=0.75) * asc_lsil_bdln_1.opts(color='#6AB35F', alpha=0.75) * asc_lsil_att.opts(color='#B34D93', alpha=0.9))
return (asclsil_bg* ref_vline * lab_vline * scatter_plot).opts(shared_axes=False)
elif column_changer == 'ASC-US/ASC-H ratio(%)' :
scatter_plot = scatterdata_filtered[scatterdata_filtered.people.str.contains('Cyto', na=False)].hvplot.scatter(x=column_changer, y='people', color='black', size=sdot_size,height= sheight,
xlim=(60,100), xticks = [min_x,lab,ref, max_x], rot=45, grid=True, title=column_changer,
xformatter='%.1f',ylabel='',xlabel='', tools = [hover_cp]).opts(fontsize = pl_title,fontscale=f_scale_lab,shared_axes=False,toolbar=None, default_tools = [])
asc_us_goal = hv.VSpan(85, 95).opts(shared_axes=False,toolbar=None, default_tools = []) #hv.VSpan(float(goal_zone[0], float(goal_zone[1]))
asc_us_bdln_1 = hv.VSpan(80, 85).opts(shared_axes=False,toolbar=None, default_tools = []) #hv.VSpan(float(borderline_zone[1][0]), float(borderline_zone[1][1]))
asc_us_bdln_2 = hv.VSpan(95, 98).opts(shared_axes=False,toolbar=None, default_tools = []) #hv.VSpan(float(borderline_zone[0][0]), float(borderline_zone[0][1]))
asc_us_att_1 = hv.VSpan(60, 80).opts(shared_axes=False,toolbar=None, default_tools = []) #hv.VSpan(float(attention_zone[0][0]), float(attention_zone[0][1]))
asc_us_att_2 = hv.VSpan(98, 100).opts(shared_axes=False, toolbar=None, default_tools=[]) #hv.VSpan(float(attention_zone[1][0]), float(attention_zone[1][1]))
ascus_bg = (asc_us_goal.opts(color='#ACFFA0', alpha = 0.75)* asc_us_att_1.opts(color='#B34D93', alpha = 0.9)* asc_us_att_2.opts(color='#B34D93', alpha = 0.9)*\
asc_us_bdln_1.opts(color='#6AB35F', alpha = 0.75)* asc_us_bdln_2.opts(color='#6AB35F', alpha = 0.75))
return (ascus_bg* ref_vline * lab_vline*scatter_plot).opts(shared_axes=False)
#elif column_changer =='Thyroid AUS Rate(%)':
# scatter_plot = scatterdata[scatterdata.people.str.contains('Cytop', na=False)].hvplot.scatter(x=column_changer, y='people', color='black', size=sdot_size,height= sheight,
# xlim=(0,30), xticks = [0,lab,10,15,30], grid=True, title=column_changer,
# xformatter='%.0f',ylabel='',xlabel='',tools = [hover_cp]).opts(fontsize = pl_title,fontscale=f_scale_lab,shared_axes=False,toolbar=None, default_tools = [])
#
# aus_goal = hv.VSpan(0, 10).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_bdln = hv.VSpan(10,15).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_att = hv.VSpan(15, 30).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_bg = (aus_goal.opts(color='#ACFFA0', alpha = 0.75)* aus_att.opts(color='#B34D93', alpha = 0.9)*\
# aus_bdln.opts(color='#6AB35F', alpha = 0.75))
#
# return (aus_bg* lab_vline * scatter_plot).opts(shared_axes=False)
#
elif column_changer == 'Abnormal Rate(%)':
scatter_plot = scatterdata_filtered[scatterdata_filtered.people.str.contains('Cytot', na=False)].hvplot.scatter(x=column_changer, y='people', color='black', size=sdot_size,height= sheight,
xlim=(0,max_x_abn), xticks = [0,lab,ref,3, max_x_abn], grid=True, title=column_changer,
xformatter='%.1f',ylabel='', xlabel='',tools = [hover_cp]).opts(fontsize = pl_title,fontscale=f_scale_lab,shared_axes=False,toolbar=None, default_tools = [])
asc_agc_goal = hv.VSpan(4.5, 6.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_bdln_1 = hv.VSpan(2.5, 4.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_bdln_2 = hv.VSpan(6.5, max_x_abn).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_att = hv.VSpan(0, 2.5).opts(shared_axes=False,toolbar=None, default_tools = [])
ascagc_bg = (asc_agc_goal.opts(color='#ACFFA0', alpha=0.75)* asc_agc_bdln_1.opts(color='#6AB35F', alpha=0.75) * \
asc_agc_bdln_2.opts(color='#6AB35F', alpha=0.75) *asc_agc_att.opts(color='#B34D93', alpha=0.9))
return (ascagc_bg* ref_vline * lab_vline*scatter_plot).opts(toolbar='above', default_tools=[]).opts(shared_axes=False)
elif column_changer == 'Referral Rate(%)':
scatter_plot = scatterdata_filtered[scatterdata_filtered.people.str.contains('Cytot', na=False)].hvplot.scatter(x=column_changer, y='people', color='black', size=sdot_size,height= sheight,
xlim=(0,max_x+0.5), xticks = [0,lab, max_x], grid=True, title=column_changer,
xformatter='%.1f',ylabel='', xlabel='',tools = [hover_cp]).opts(fontsize = pl_title,fontscale=f_scale_lab,shared_axes=False,toolbar=None, default_tools = [])
ref_goal = hv.VSpan(7, 13).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bdln_1 = hv.VSpan(5, 7).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bdln_2 = hv.VSpan(13, 15).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_att_1 = hv.VSpan(15, max_x+0.5).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_att_2 = hv.VSpan(0, 5).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bg = (ref_goal.opts(color='#ACFFA0', alpha=0.75)* ref_bdln_1.opts(color='#6AB35F', alpha=0.75) * \
ref_bdln_2.opts(color='#6AB35F', alpha=0.75)) *ref_att_1.opts(color='#B34D93', alpha = 0.9) *\
ref_att_2.opts(color='#B34D93', alpha = 0.9)
return (ref_bg * lab_vline * scatter_plot).opts(toolbar='above', default_tools=[]).opts(shared_axes=False)
elif column_changer == 'hrHPV/ASC-US rate (%)':
ref_value = 36.6
ref_hline = hv.HLine(y=ref_value).opts(color='black', alpha=1, line_width=2).opts(shared_axes=False, toolbar=None, default_tools=[])
line_plot = line_data.hvplot.line(x='year', y=column_changer, color='#5B91D1', line_dash ='dashed', line_width=2, height=600, title=column_changer, xlabel='Year', ylabel='hrHPV positivity (%)', tools=[hover_cp]).opts(fontsize=pl_title, fontscale=f_scale_lab, shared_axes=False, toolbar=None, default_tools=[], ylim=(10, 90))
selected_year = line_data[line_data['year'] == year_slider]
point = hv.Points(selected_year, ['year', column_changer]).opts(color='black', size=20, marker='o', line_color='black')
hpvascus_goal = hv.HSpan(30, 60).opts(shared_axes=False, toolbar=None, default_tools = [])
hpvascus_bdln_1 = hv.HSpan(20, 30).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvascus_bdln_2 = hv.HSpan(60, 70).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvascus_att_1 = hv.HSpan(0, 20).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvascus_att_2 = hv.HSpan(70, 100).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvascus_bg = (hpvascus_goal.opts(color='#ACFFA0', alpha=0.75) * hpvascus_bdln_1.opts(color='#6AB35F', alpha=0.75) * hpvascus_bdln_2.opts(color='#6AB35F', alpha=0.75)) * hpvascus_att_1.opts(color='#B34D93', alpha = 0.9) * hpvascus_att_2.opts(color='#B34D93', alpha = 0.9)
return (hpvascus_bg * ref_hline * line_plot * point).opts(toolbar='above', default_tools=[]).opts(shared_axes=False)
elif column_changer == 'hrHPV Prevalence (%)':
ref_value = 21
ref_hline = hv.HLine(y=ref_value).opts(color='black', alpha=1, line_width=2).opts(shared_axes=False, toolbar=None, default_tools=[])
line_plot = line_data.hvplot.line(x='year', y=column_changer, color='#5B91D1', line_dash ='dashed', line_width=2, height=600, title=column_changer, xlabel='Year', ylabel='hrHPV positivity (%)', tools=[hover_cp]).opts(fontsize=pl_title, fontscale=f_scale_lab, shared_axes=False, toolbar=None, default_tools=[], ylim=(0, 50))
selected_year = line_data[line_data['year'] == year_slider]
point = hv.Points(selected_year, ['year', column_changer]).opts(color='black', size=20, marker='o', line_color='black')
hpvprev_goal = hv.HSpan(11, 29).opts(shared_axes=False, toolbar=None, default_tools = [])
hpvprev_bdln_1 = hv.HSpan(5, 11).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvprev_bdln_2 = hv.HSpan(29, 35).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvprev_att_1 = hv.HSpan(0, 5).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvprev_att_2 = hv.HSpan(35, 50).opts(shared_axes=False,toolbar=None, default_tools = [])
hpvprev_bg = (hpvprev_goal.opts(color='#ACFFA0', alpha=0.75) * hpvprev_bdln_1.opts(color='#6AB35F', alpha=0.75) * hpvprev_bdln_2.opts(color='#6AB35F', alpha=0.75)) * hpvprev_att_1.opts(color='#B34D93', alpha = 0.9) * hpvprev_att_2.opts(color='#B34D93', alpha = 0.9)
return (hpvprev_bg * ref_hline * line_plot * point).opts(toolbar='above', default_tools=[]).opts(shared_axes=False)
def add_no_cases_lab(year_slider):
scatterdata = df[(df.year == year_slider)&(df.people.str.contains('KGYC'))].sort_values(by='year').reset_index(drop=True)
gyncase_no = scatterdata['Number'].iloc[0]
#thycase_no = scatterdata['ThyAC Number'].iloc[0]
return pn.pane.Markdown(f""" **Case number: {gyncase_no}** """,
styles = {'font-size':fpx})
interactive_no_lab = pn.bind(add_no_cases_lab, year_slider)
interactive_func = pn.bind(lab_management, year_slider, column_changer)
#COLORS (EGGPLANT): ['#ACFFA0','#6AB35F','#B34D93']
# In[9]:
unique_values = df[df['people'].str.contains('Cytot', na=False)]
unique_values = unique_values.sort_values(by='people',ascending=True,key=natsort_keygen())
technician_list = unique_values[~unique_values['people'].str.contains('Cytotechnologist 8', case=False, na=False)]['people'].unique().tolist()
technician_list
ct_select = pn.widgets.Select(name='Select', options=technician_list, value = 'Cytotechnologist 1', styles = mobile_style, width = 400, height=150, stylesheets= [select_style])
#Overviews
# # CT ABNORMAL RATE OVERVIEW:
def abnormal_rate_overview_ct(ct_select):
ct_result = df[df['people'] == ct_select][['Abnormal Rate(%)','year']]
if len(ct_result) > 1:
last_result = ct_result['Abnormal Rate(%)'].iloc[-1]
second_last_result = ct_result['Abnormal Rate(%)'].iloc[-2]
last_year = ct_result['year'].iloc[-1]
second_last_year = ct_result['year'].iloc[-2]
if 4 < last_result < 7:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.1f}**."
ct_comment_2 = ""
elif (2.5 < last_result < 4) or (7 < last_result < 8.5):
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}**."
if last_result < second_last_result:
ct_comment_2 = f"A decrease in the abnormal rate can be observed compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
else:
ct_comment_2 =""
elif last_result < 2.5:
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}%**, your abnormal rate is too low. You use the cytology abnormality categories to rarely, that means an elevated risk in overlooking them! "
if last_result > second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your abnormal rate worsened compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
elif last_result > 8.5:
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}%**, your abnormal rate is too high. You are using the abnormality categories too much."
if last_result < second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your abnormal rate worsened compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
return pn.pane.Markdown(f"""
## Abnormal Rate Overview:
**Description:** *The abnormal Rate represents the relative ratio of ASC, SIL and AGC diagnoses. Alterations ( Z Score: <-2) means a level of insecurity that requires attention.*<br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return pn.pane.Markdown("""
### Not enough data to calculate parameters.
""", styles={'font-size': '24px'})
inter_abnorm_ct = pn.bind(abnormal_rate_overview_ct, ct_select)
# # CT ASCUS RATE OVERVIEW:
def ascus_rate_overview_ct(ct_select):
ct_result = df[df['people'] == ct_select][['ASC-US/ASC-H ratio(%)','year']]
if len(ct_result) > 1:
last_result = ct_result['ASC-US/ASC-H ratio(%)'].iloc[-1]
last_year = ct_result['year'].iloc[-1]
second_last_result = ct_result['ASC-US/ASC-H ratio(%)'].iloc[-2]
second_last_year = ct_result['year'].iloc[-2]
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore #Végül 95%-os CI-vel alkottam meg a bdl és att zónákat
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)]
if goal_zone[0] <= last_result <= goal_zone[1]:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.0f}%/{(100-last_result):.0f}%**."
ct_comment_2 = ""
elif any(lower <= last_result < upper for lower, upper in borderline_zone):
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.0f}%/{(100-last_result):.0f}%**."
if last_result < second_last_result:
ct_comment_2 = f"A decrease in the ASC-US/ASC-H ratio can be observed since last record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**."
else:
ct_comment_2 =""
elif any(lower <= last_result <= upper for lower, upper in attention_zone):
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.0f}%/{(100-last_result):.0f}%**, your ASC-US/ASC-H ratio is too low. You use the ASC-US category very rarely and/or you use the ASC-H category too often. "
if last_result > second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your ASC-US/ASC-H ratio worsened since your last record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**."
return pn.pane.Markdown(f"""
## ASC-US/ASC-H ratio Overview:
**Description:** *ASC-US/ASC-H ratio literature reference value 90% / 10%. Given our average laboratory Z score of {avg_zscore:.2f}, the laboratory's average ratio is {lab_ascus_reference:.2f}% / {lab_asch_reference:.2f}%.* <br>
Given our average laboratory Z score, the goal, borderline, and attention zones calculated with 95% CI are the following: **Goal Zone:** {goal_zone[0]:.2f} to {goal_zone[1]:.2f}, **Borderline Zone:** {borderline_zone[0][0]:.2f} to {borderline_zone[0][1]:.2f} and {borderline_zone[1][0]:.2f} to {borderline_zone[1][1]:.2f}, **Attention Zone:** {attention_zone[0][0]:.2f} to {attention_zone[0][1]:.2f} and {attention_zone[1][0]:.2f} to {attention_zone[1][1]:.2f}. <br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return None
inter_ascus_ct = pn.bind(ascus_rate_overview_ct, ct_select)
# # CT ASC/LSIL OVERVIEW:
def asclsil_rate_overview_ct(ct_select):
ct_result = df[df['people'] == ct_select][['ASC/LSIL','year']]
if len(ct_result) > 1:
last_result = ct_result['ASC/LSIL'].iloc[-1]
second_last_result = ct_result['ASC/LSIL'].iloc[-2]
last_year = ct_result['year'].iloc[-1]
second_last_year = ct_result['year'].iloc[-2]
if 1.5 < last_result < 3:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.1f}**."
ct_comment_2 = ""
elif last_result < 1.5:
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}**. This means your ASC/LSIL is low, and there is a higher risk of you overlooking abnormalities."
if last_result < second_last_result:
ct_comment_2 = f"A decrease in the ASC/LSIL ratio can be observed since last record **({second_last_result:.1f})** from **{second_last_year}**. Please consult with a cytopathologist"
else:
ct_comment_2 =""
elif 3 < last_result < 3.75:
ct_comment = f"Your latest record from **{last_year}**is in the laboratory borderline zone **{last_result:.1f}**."
if last_result > second_last_result:
ct_comment_2 = f"An increase in the ASC/LSIL ratio can be observed since last record **({second_last_result:.1f})** from **{second_last_year}**."
else:
ct_comment_2 =""
elif last_result > 3.75:
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}**, your ASC/LSIL ratio is too high. You use the ASC category too often. "
if last_result < second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f})** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your ASC/LSIL ratio worsened since your last record **({second_last_result:.1f})** from **{second_last_year}**."
return pn.pane.Markdown(f"""
## ASC/LSIL Overview:
**Description:** *ASC/LSIL value represents the ASC-US, ASC-H diagnoses compared to LSIL diagnoses. Considerable elevation (Z Score > 2) from reference value {df.iat[0,5]} requires attention. A lower result than 1.5 could imply an increase in false negative interpretations.*<br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return None
inter_asclsil_ct = pn.bind(asclsil_rate_overview_ct, ct_select)
# # CT REFERRAL OVERVIEW
# In[50]:
def referral_rate_overview_ct(ct_select):
ct_result = df[df['people'] == ct_select][['Referral Rate(%)','year']]
if len(ct_result) > 1:
last_result = ct_result['Referral Rate(%)'].iloc[len(ct_result)-1]
second_last_result = ct_result['Referral Rate(%)'].iloc[-2]
last_year = ct_result['year'].iloc[-1]
second_last_year = ct_result['year'].iloc[-2]
if 7 < last_result < 13:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.1f}%**."
ct_comment_2 = ""
elif 5 < last_result < 7:
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}%**."
if last_result < second_last_result:
ct_comment_2 = f"An underreferral tendency can be observed since your last record **({second_last_result:.f}%)** from **{second_last_year}**."
else:
ct_comment_2 =""
elif 13 < last_result < 15:
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}%**."
if last_result > second_last_result:
ct_comment_2 = f"An overreferral tendency can be observed since your last record **({second_last_result:.f}%)** from **{second_last_year}**."
else:
ct_comment_2 =""
elif last_result < 5:
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}%**, your referral rate is too low. You might overlook cytology abnormalities! "
if last_result > second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**. Please consult with a cytopathologist about your result."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your referral rate worsened since your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
elif last_result > 15:
ct_comment = f"Your latest registered result is **{last_result:.1f}%** from **{last_year}**, your referral rate is high."
ct_comment_2 = ""
return pn.pane.Markdown(f"""
## Referral Rate Overview:
**Description:** *Referral Rate signifies the ratio the Cytotechnologist consults cases. < 5% or > 15% requires attention.*<br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return None
inter_referral_ct = pn.bind(referral_rate_overview_ct, ct_select)
# In[10]:
def update_scatter_ct(year_slider,ct_select):
scatterdata = df[(df.year == year_slider)&(df['people'] == ct_select)]
if len(scatterdata) > 0:
labdata = df[df.year == year_slider]
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore #Végül 95%-os CI-vel alkottam meg a bdl és att zónákat
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)]
#ASC/#LSIL
ref_lsil = df['ASC/LSIL'].iloc[0]
lab_lsil = labdata.iloc[0]['ASC/LSIL']
max_x_lsil = labdata['ASC/LSIL'].max()
tooltips_asc_lsil = f"""<div><span style="font-size: {fpx}; font-weight: bold">@{{ASC/LSIL}} </span></div>"""
hover_asc_lsil = HoverTool(tooltips=tooltips_asc_lsil)
scatter_lsil = scatterdata.iloc[0:1].hvplot.scatter(x='ASC/LSIL', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=(0,max_x_lsil+0.5), xticks = [0,lab_lsil,ref_lsil, 5], yaxis = 'bare',grid=True, title='ASC/LSIL',
xformatter='%.1f',xlabel ='', ylabel='', tools = [hover_asc_lsil]).opts(fontsize = pl_title,shared_axes=False,toolbar=None, default_tools = [])
ref_lsil_vline = hv.VLine(x=ref_lsil).opts(color='black', alpha = 1).opts(fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
lab_lsil_vline = hv.VLine(x=lab_lsil).opts(color='#5B91D1', line_dash ='dashed', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_goal = hv.VSpan(1.5, 3).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_1 = hv.VSpan(3, 3.75).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_2 = hv.VSpan(0, 1.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_att = hv.VSpan(3.75, 15).opts(shared_axes=False,toolbar=None, default_tools = [])
asclsil_bg =(asc_lsil_goal.opts(color='#ACFFA0', alpha=0.75) * asc_lsil_bdln_2.opts(color='#6AB35F', alpha=0.75) * asc_lsil_bdln_1.opts(color='#6AB35F', alpha=0.75) * asc_lsil_att.opts(color='#B34D93', alpha=0.9))
#ASC-US/ASC-H ratio(%)
ref_ascus = df['ASC-US/ASC-H ratio(%)'].iloc[0]
lab_ascus = labdata['ASC-US/ASC-H ratio(%)'].iloc[0]
max_x_ascus = labdata['ASC-US/ASC-H ratio(%)'].max()
min_x_ascus = labdata['ASC-US/ASC-H ratio(%)'].min()
tooltips_ascus = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{ASC-US/ASC-H ratio(%)}} </span></div>"""
hover_ascus = HoverTool(tooltips=tooltips_ascus)
scatter_ascus = scatterdata.iloc[0:1].hvplot.scatter(x='ASC-US/ASC-H ratio(%)', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=(17,100), xticks = [min_x_ascus,lab_ascus,ref_ascus, 100], yaxis = 'bare', grid=True, title='ASC-US/ASC-H ratio(%)',
xformatter='%.0f',xlabel ='', ylabel='', tools = [hover_ascus]).opts(fontsize = pl_title,shared_axes=False,toolbar=None, default_tools = [])
ref_ascus_vline = hv.VLine(x=ref_ascus).opts(color='black', alpha = 1).opts(fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
lab_ascus_vline = hv.VLine(x=lab_ascus).opts(color='#5B91D1', line_dash ='dashed', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_goal = hv.VSpan(float(goal_zone[0]), float(goal_zone[1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_bdln_1 = hv.VSpan(float(borderline_zone[1][0]), float(borderline_zone[1][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_bdln_2 = hv.VSpan(float(borderline_zone[0][0]), float(borderline_zone[0][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_att_1 = hv.VSpan(float(attention_zone[0][0]), float(attention_zone[0][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_att_2 = hv.VSpan(float(attention_zone[1][0]), float(attention_zone[1][1])).opts(shared_axes=False, toolbar=None, default_tools=[])
ascus_bg = (asc_us_goal.opts(color='#ACFFA0', alpha = 0.75)* asc_us_att_1.opts(color='#B34D93', alpha = 0.9)* asc_us_att_2.opts(color='#B34D93', alpha = 0.9)*\
asc_us_bdln_1.opts(color='#6AB35F', alpha = 0.75)* asc_us_bdln_2.opts(color='#6AB35F', alpha = 0.75))
#LSIL, ASC-US, AGC(%)
ref_agc = df['Abnormal Rate(%)'].iloc[0]
lab_agc = labdata['Abnormal Rate(%)'].iloc[0]
max_x_agc = labdata[labdata.people.str.contains('ytolog')]['Abnormal Rate(%)'].max()
tooltips_agc = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{Abnormal Rate(%)}} </span></div>"""
hover_agc = HoverTool(tooltips=tooltips_agc)
scatter_agc = scatterdata.iloc[0:1].hvplot.scatter(x='Abnormal Rate(%)', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=(0,max_x_agc), xticks = [0,lab_agc,ref_agc], yaxis = 'bare',grid=True, title='Abnormal Rate(%)',
xformatter='%.1f',xlabel ='', ylabel='', tools= [hover_agc]).opts(fontsize = pl_title,shared_axes=False,toolbar=None, default_tools = [])
ref_agc_vline = hv.VLine(x=ref_agc).opts(color='black', alpha = 1).opts(fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
lab_agc_vline = hv.VLine(x=lab_agc).opts(color='#5B91D1', line_dash ='dashed', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_goal = hv.VSpan(4.5, 6.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_bdln_1 = hv.VSpan(2.5, 4.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_bdln_2 = hv.VSpan(6.5, max_x_agc).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_agc_att = hv.VSpan(0, 2.5).opts(shared_axes=False,toolbar=None, default_tools = [])
ascagc_bg = (asc_agc_goal.opts(color='#ACFFA0', alpha=0.75)* asc_agc_bdln_1.opts(color='#6AB35F', alpha=0.75) * \
asc_agc_bdln_2.opts(color='#6AB35F', alpha=0.75) *asc_agc_att.opts(color='#B34D93', alpha=0.9))
#Referral Rate(%)
scatterdata_filtered = scatterdata[~scatterdata['people'].str.contains('Cytotechnologist 8', case=False, na=False)]
labdata_filtered = labdata[~labdata['people'].str.contains('Cytotechnologist 8', case=False, na=False)]
lab_ref_rate = labdata_filtered['Referral Rate(%)'].iloc[0]
max_x_ref_rate = labdata_filtered['Referral Rate(%)'].max()
tooltips_ref = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{Referral Rate(%)}} </span></div>"""
hover_ref = HoverTool(tooltips=tooltips_ref)
scatter_ref_rate = scatterdata_filtered.iloc[0:1].hvplot.scatter(x='Referral Rate(%)', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=(0,max_x_ref_rate+1), xticks = [0,lab_ref_rate,max_x_ref_rate], yaxis = 'bare',grid=True, title='Referral Rate(%)',
xformatter='%.1f',xlabel ='', ylabel='', tools = [hover_ref]).opts(fontsize = pl_title,fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
lab_ref_rate_vline = hv.VLine(x=lab_ref_rate).opts(color='#5B91D1', line_dash ='dashed', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_goal = hv.VSpan(7, 13).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bdln_1 = hv.VSpan(5, 7).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bdln_2 = hv.VSpan(13, 15).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_att_1 = hv.VSpan(15, max_x_ref_rate+1).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_att_2 = hv.VSpan(0, 5).opts(shared_axes=False,toolbar=None, default_tools = [])
ref_bg = ref_goal.opts(color='#ACFFA0', alpha=0.75)* ref_bdln_1.opts(color='#6AB35F', alpha=0.75) * \
ref_bdln_2.opts(color='#6AB35F', alpha=0.75) *ref_att_1.opts(color='#B34D93', alpha=0.9)*\
ref_att_2.opts(color='#B34D93', alpha=0.9)
return ((asclsil_bg* ref_lsil_vline * lab_lsil_vline * scatter_lsil).opts(toolbar='above') +\
(ascus_bg* ref_ascus_vline * lab_ascus_vline*scatter_ascus) +\
(ascagc_bg* ref_agc_vline * lab_agc_vline*scatter_agc) + \
(ref_bg * lab_ref_rate_vline * scatter_ref_rate)).opts(shared_axes=False).cols(1)
else:
return pn.pane.Markdown('# N/A', styles = {'font-size':'20px'})
#COLORS (EGGPLANT): ['#ACFFA0','#6AB35F','#B34D93']
def add_no_cases_ct(year_slider,ct_select):
scatterdata = df[(df.year == year_slider)&(df['people'] == ct_select)]
if len(scatterdata) > 0:
case_no = scatterdata['Number'].iloc[0]
return pn.pane.Markdown(f'# Cases examined: {case_no}')
else:
return None
interactive_no_ct = pn.bind(add_no_cases_ct,year_slider, ct_select)
interactive_qa_ct = pn.bind(update_scatter_ct,year_slider, ct_select)
ct_performance = pn.Tabs(('Performance results',pn.Column(pn.Row(ct_select, year_slider), interactive_no_ct, \
pn.Row(pn.Column(pn.pane.Markdown("<br><br><br>"),vertical_legend_indiv),interactive_qa_ct, height = 600),\
pn.Column(pn.pane.Markdown("<br><br><br>"),modal_btn))),\
('Results overview',pn.Column(ct_select, inter_asclsil_ct, inter_ascus_ct, inter_abnorm_ct, inter_referral_ct)),styles = {'font-size': '20px'})
# In[11]:
unique_values_cp = df[df['people'].str.contains('Cytopath', na=False)]
unique_values_cp = unique_values_cp.sort_values(by='people',ascending=True,key=natsort_keygen())
citopath_list = unique_values_cp['people'].unique().tolist()
cp_select = pn.widgets.Select(name='Select', options=citopath_list, value ='Cytopathologist 1', styles = mobile_style, stylesheets= [select_style], width = 400, height = 150)
# # CP ASCUS RATE OVERVIEW:
def ascus_rate_overview_cp(cp_select):
ct_result = df[df['people'] == cp_select][['ASC-US/ASC-H ratio(%)','year']]
if len(ct_result) > 1:
last_result = ct_result['ASC-US/ASC-H ratio(%)'].iloc[-1]
last_year = ct_result['year'].iloc[-1]
second_last_result = ct_result['ASC-US/ASC-H ratio(%)'].iloc[-2]
second_last_year = ct_result['year'].iloc[-2]
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore #Végül 95%-os CI-vel alkottam meg a bdl és att zónákat
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)]
if goal_zone[0] <= last_result <= goal_zone[1]:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.0f}%/{(100-last_result):.0f}%**."
ct_comment_2 = ""
elif any(lower <= last_result < upper for lower, upper in borderline_zone):
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.0f}%/{(100-last_result):.0f}%**."
if last_result < second_last_result:
ct_comment_2 = f"A decrease in the ASC-US/ASC-H ratio can be observed since last record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**."
else:
ct_comment_2 =""
elif any(lower <= last_result < upper for lower, upper in attention_zone):
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.0f}%/{(100-last_result):.0f}%**, your ASC-US/ASC-H ratio is too low. You use the ASC-US category very rarely and/or you use the ASC-H category too often. "
if last_result > second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your ASC-US/ASC-H ratio worsened since your last record **({second_last_result:.0f}%/{(100-second_last_result):.0f}%)** from **{second_last_year}**."
return pn.pane.Markdown(f"""
## ASC-US/ASC-H ratio Overview:
**Description:** *ASC-US/ASC-H ratio literature reference value 90% / 10%. Given our average laboratory Z score of {avg_zscore:.2f}, the laboratory's average ratio is {lab_ascus_reference:.2f}% / {lab_asch_reference:.2f}%.* <br>
Given our average laboratory Z score, the goal, borderline, and attention zones calculated with 95% CI are the following: **Goal Zone:** {goal_zone[0]:.2f} to {goal_zone[1]:.2f}, **Borderline Zone:** {borderline_zone[0][0]:.2f} to {borderline_zone[0][1]:.2f} and {borderline_zone[1][0]:.2f} to {borderline_zone[1][1]:.2f}, **Attention Zone:** {attention_zone[0][0]:.2f} to {attention_zone[0][1]:.2f} and {attention_zone[1][0]:.2f} to {attention_zone[1][1]:.2f}. <br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return None
inter_ascus_cp = pn.bind(ascus_rate_overview_ct, cp_select)
# # CP ASC/LSIL OVERVIEW:
def asclsil_rate_overview_cp(cp_select):
ct_result = df[df['people'] == cp_select][['ASC/LSIL','year']]
if len(ct_result) > 1:
last_result = ct_result['ASC/LSIL'].iloc[-1]
second_last_result = ct_result['ASC/LSIL'].iloc[-2]
last_year = ct_result['year'].iloc[-1]
second_last_year = ct_result['year'].iloc[-2]
if 1.5 < last_result < 3:
ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.1f}**."
ct_comment_2 = ""
elif last_result < 1.5:
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}**."
if last_result < second_last_result:
ct_comment_2 = f"A decrease in the ASC/LSIL ratio can be observed since last record **({second_last_result:.1f})** from **{second_last_year}**. Please consult with a cytopathologist."
else:
ct_comment_2 =""
elif 3 < last_result < 3.75:
ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}**."
if last_result > second_last_result:
ct_comment_2 = f"An increase in the ASC/LSIL ratio can be observed since previous record **({second_last_result:.1f})** from **{second_last_year}**."
else:
ct_comment_2 =""
elif last_result > 3.75:
ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}**, your ASC/LSIL ratio is too high. You use the ASC category too often. "
if last_result < second_last_result:
ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f})** from **{second_last_year}**, but please consult with a cytopathologist."
else:
ct_comment_2 = f"<br>Please consult with a cytopathologist about your result since your ASC/LSIL ratio worsened since your last record **({second_last_result:.1f})** from **{second_last_year}**."
return pn.pane.Markdown(f"""
## ASC/LSIL Overview:
**Description:** *ASC/LSIL value represents the ASC-US, ASC-H diagnoses compared to LSIL diagnoses. Considerable elevation (Z Score > 2) from reference value {df.iat[0,5]} requires attention. A lower result than 1.5 could imply an increase in false negative interpretations.*<br><br>
**Result:** {ct_comment} {ct_comment_2}
""", sizing_mode ="stretch_width", styles={'font-size': fpx})
else:
return pn.pane.Markdown("""
## Not enough data to calculate parameters.
""", styles = {'font-size':fpx})
inter_asclsil_cp = pn.bind(asclsil_rate_overview_cp, cp_select)
# # CP THYROID AUS RATE OVERVIEW
# In[57]:
#def thyroid_rate_overview_cp(cp_select):
# ct_result = df[df['people'] == cp_select][['Thyroid AUS Rate(%)','year']]
# if ct_result['Thyroid AUS Rate(%)'].sum() > 0.0:
#
# if len(ct_result) > 1:
# last_result = ct_result['Thyroid AUS Rate(%)'].iloc[-1]
# second_last_result = ct_result['Thyroid AUS Rate(%)'].iloc[-2]
# last_year = ct_result['year'].iloc[-1]
# second_last_year = ct_result['year'].iloc[-2]
# if 0 < last_result < 10:
# ct_comment = f"Your latest result from **{last_year}** is within the laboratory goal zone **{last_result:.1f}%**."
# ct_comment_2 = ""
# elif 10 <last_result < 15:
# ct_comment = f"Your latest record from **{last_year}** is in the laboratory borderline zone **{last_result:.1f}%**."
# if last_result > second_last_result:
# ct_comment_2 = f"A decrease in the thyroid AUS ratio can be observed compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
# else:
# ct_comment_2 =""
#elif last_result > 15:
# ct_comment = f"Your latest registered result from **{last_year}** is **{last_result:.1f}%**, your thyroid AUS ratio is too high. You use the AUS (TBS 3) category too much. "
# if last_result < second_last_result:
# ct_comment_2 = f"You performed better compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**, but please consult with an experienced cytopathologist."
#else:
# ct_comment_2 = f"<br>Please consult with an experienced cytopathologist about your result since your thyroid AUS ratio worsened compared to your previous record **({second_last_result:.1f}%)** from **{second_last_year}**."
#
#
#
# return pn.pane.Markdown(f"""
# ## Thyroid AUS Rate(%) Overview:
# *Thyroid AUS Rate signifies the ratio of TBS 3 cytology reports. More than 15% requires attention.* <br><br>
# {ct_comment} {ct_comment_2}
# """, sizing_mode ="stretch_width", styles = {'font-size':fpx})
# else:
# return pn.pane.Markdown("""
# ## Not enough data to calculate parameters.
# """, styles = {'font-size':fpx})
#else:
# return pn.pane.Markdown("""
# ## Current cytopathologist doesn't examine thyroid FNA cases.
# """, styles = {'font-size':fpx})
#inter_thyroid_cp = pn.bind(thyroid_rate_overview_cp, cp_select)
# In[12]:
# In[13]:
def update_scatter(year_slider,cp_select):
avg_zscore = zscore_eval.get_group('KGYC')['ASC-US/ASC-H ratio(%)'].mean()
lab_ascus_reference = 90.0 - avg_zscore #Végül 95%-os CI-vel alkottam meg a bdl és att zónákat
lab_asch_reference = 10.0 + avg_zscore
sd_dev = 5 #(95% CI esetén)
goal_zone = (lab_ascus_reference - sd_dev, lab_ascus_reference + sd_dev)
borderline_zone = [(lab_ascus_reference - 2 * sd_dev, lab_ascus_reference - sd_dev), (lab_ascus_reference + sd_dev, lab_ascus_reference + 2 * sd_dev)]
attention_zone = [(lab_ascus_reference - 2 * sd_dev, 0), (lab_ascus_reference + 2 * sd_dev, 100)]
scatterdata = df[(df.year == year_slider)&(df['people'] == cp_select)]
if len(scatterdata) > 0:
#thy_exist = scatterdata['Thyroid AUS Rate(%)'].iloc[0]
labdata = df[(df.year == year_slider)]
ref_lsil = df['ASC/LSIL'].iloc[0]
lab_lsil = labdata['ASC/LSIL'].iloc[0]
max_x_lsil = labdata['ASC/LSIL'].max()
xlim = (0, max_x_lsil) if max_x_lsil > 7 else (0,7)
xticks = [0,lab_lsil,ref_lsil, max_x_lsil]
tooltips_asc_lsil = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{ASC/LSIL}} </span></div>"""
hover_asc_lsil = HoverTool(tooltips=tooltips_asc_lsil)
scatter_lsil = scatterdata.iloc[0:1].hvplot.scatter(x='ASC/LSIL', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=xlim, xticks = xticks, yaxis = 'bare',grid=True, title='ASC/LSIL',
xformatter='%.1f',xlabel ='', ylabel='', tools = [hover_asc_lsil]).opts(fontsize = pl_title,fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
ref_lsil_vline = hv.VLine(x=ref_lsil).opts(color='black', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
lab_lsil_vline = hv.VLine(x=lab_lsil).opts(color='#5B91D1', line_dash ='dashed', alpha=1).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_goal = hv.VSpan(1.5, 3).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_1 = hv.VSpan(3, 3.75).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_bdln_2 = hv.VSpan(0, 1.5).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_lsil_att = hv.VSpan(3.75, 15).opts(shared_axes=False,toolbar=None, default_tools = [])
asclsil_bg =(asc_lsil_goal.opts(color='#ACFFA0', alpha =0.75) * asc_lsil_bdln_2.opts(color='#6AB35F', alpha =0.75) * asc_lsil_bdln_1.opts(color='#6AB35F', alpha =0.75) * asc_lsil_att.opts(color='#B34D93', alpha =0.9))
ref_ascus = df['ASC-US/ASC-H ratio(%)'].iloc[0]
lab_ascus = labdata['ASC-US/ASC-H ratio(%)'].iloc[0]
max_x_ascus = labdata['ASC-US/ASC-H ratio(%)'].max()
min_x_ascus = labdata['ASC-US/ASC-H ratio(%)'].min()
tooltips_ascus = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{ASC-US/ASC-H ratio(%)}} </span></div>"""
hover_ascus = HoverTool(tooltips=tooltips_ascus)
scatter_ascus = scatterdata.iloc[0:1].hvplot.scatter(x='ASC-US/ASC-H ratio(%)', y='people', color='black', size=sdot_person,height= 160, width=800,
xlim=(17,100), xticks = [min_x_ascus,lab_ascus,ref_ascus, 100], yaxis = 'bare', grid=True, title='ASC-US/ASC-H ratio(%)',
xformatter='%.0f',xlabel ='', ylabel='', tools=[hover_ascus]).opts(fontsize = pl_title,fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
ref_ascus_vline = hv.VLine(x=ref_ascus).opts(color='black', alpha = 1).opts(shared_axes=False,toolbar=None, default_tools = [])
lab_ascus_vline = hv.VLine(x=lab_ascus).opts(color='#5B91D1', line_dash ='dashed', alpha=1).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_goal = hv.VSpan(float(goal_zone[0]), float(goal_zone[1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_bdln_1 = hv.VSpan(float(borderline_zone[1][0]), float(borderline_zone[1][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_bdln_2 = hv.VSpan(float(borderline_zone[0][0]), float(borderline_zone[0][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_att_1 = hv.VSpan(float(attention_zone[0][0]), float(attention_zone[0][1])).opts(shared_axes=False,toolbar=None, default_tools = [])
asc_us_att_2 = hv.VSpan(float(attention_zone[1][0]), float(attention_zone[1][1])).opts(shared_axes=False, toolbar=None, default_tools=[])
ascus_bg = (asc_us_goal.opts(color='#ACFFA0', alpha = 0.75)* asc_us_att_1.opts(color='#B34D93', alpha = 0.9)* asc_us_att_2.opts(color='#B34D93', alpha = 0.9)*\
asc_us_bdln_1.opts(color='#6AB35F', alpha = 0.75)* asc_us_bdln_2.opts(color='#6AB35F', alpha = 0.75))
#if pd.isna(scatterdata['Thyroid AUS Rate(%)'].iloc[0]):
# return ((asclsil_bg* ref_lsil_vline * lab_lsil_vline * scatter_lsil).opts(toolbar='above') +\
# (ascus_bg* ref_ascus_vline * lab_ascus_vline*scatter_ascus).opts(toolbar='above')).cols(1).opts(shared_axes=False)
#else:
# ref_aus = df['Thyroid AUS Rate(%)'].iloc[0]
# lab_aus = labdata['Thyroid AUS Rate(%)'].iloc[0]
# max_x_aus = labdata['Thyroid AUS Rate(%)'].max()
# tooltips_aus = f"""<div><span style="font-size: {fpx};font-weight: bold">@{{Thyroid AUS Rate(%)}} </span></div>"""
# hover_aus = HoverTool(tooltips=tooltips_aus)
# scatter_aus = scatterdata.iloc[0:1].hvplot.scatter(x='Thyroid AUS Rate(%)', y='people', color='black', size=sdot_person,height= 160,
# xlim=(0,30), xticks = [0,lab_aus,10,15,30], yaxis = 'bare',grid=True, title='Thyroid AUS Rate(%)',
# xformatter='%.1f',xlabel ='', ylabel='', tools = [hover_aus]).opts(fontsize = pl_title,fontscale=f_scale, shared_axes=False,toolbar=None, default_tools = [])
# lab_aus_vline = hv.VLine(x=lab_aus).opts(color='#5B91D1', line_dash ='dashed', alpha=1).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_goal = hv.VSpan(0, 10).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_bdln = hv.VSpan(10,15).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_att = hv.VSpan(15, 30).opts(shared_axes=False,toolbar=None, default_tools = [])
# aus_bg = (aus_goal.opts(color='#ACFFA0', alpha = 0.75)* aus_att.opts(color='#B34D93', alpha = 0.9)*\
# aus_bdln.opts(color='#6AB35F', alpha = 0.75))
return ((asclsil_bg* ref_lsil_vline * lab_lsil_vline * scatter_lsil).opts(toolbar='above') +\
(ascus_bg* ref_ascus_vline * lab_ascus_vline*scatter_ascus).opts(toolbar='above')).cols(1).opts(shared_axes=False)
else:
return pn.pane.Markdown("# N/A", styles={'font-size':'20px'})
#COLORS (EGGPLANT): ['#ACFFA0','#6AB35F','#B34D93']
def add_no_cases_cp(year_slider,cp_select):
scatterdata = df[(df.year == year_slider)&(df['people'] == cp_select)]
if len(scatterdata) > 0:
case_no = scatterdata['Number'].iloc[0]
#if pd.isna(scatterdata['Thyroid AUS Rate(%)'].iloc[0]):
# return pn.pane.Markdown(f'# Gyn Cases examined: {case_no}')
#else:
# ac_no =scatterdata['ThyAC Number'].iloc[0]
return pn.pane.Markdown(f'# Gyn Cases examined: {case_no}')
else:
return None
interactive_qa = pn.bind(update_scatter,year_slider, cp_select)
interactive_no_cp = pn.bind(add_no_cases_cp,year_slider,cp_select)
cp_performance = pn.Tabs(('Performance results',pn.Column(pn.Row(cp_select,year_slider),interactive_no_cp,\
pn.Row(pn.Column(pn.pane.Markdown("<br><br>"),vertical_legend_indiv),interactive_qa, height = 600 )\
,pn.Column(pn.pane.Markdown("<br><br>"),modal_btn)))\
,('Results overview',pn.Column(cp_select, inter_asclsil_cp, inter_ascus_cp)),styles = {'font-size': '20px'})
# In[ ]:
# In[ ]:
# In[14]:
results = df[df['year'] != 2017]
results_filtered = results[~results['people'].str.contains('Cytotechnologist 8', na=False)]
med_ref_distribution = pd.concat([results_filtered[results_filtered.people.str.contains('KGYC')],results.iloc[[0]]])[['ASC/LSIL','ASC-US/ASC-H ratio(%)', 'Abnormal Rate(%)','Referral Rate(%)', 'hrHPV/ASC-US rate (%)', 'hrHPV Prevalence (%)']]
med_ref_distribution.loc[len(med_ref_distribution)] = {'ASC/LSIL':0.75,'ASC-US/ASC-H ratio(%)':12.5, 'Abnormal Rate(%)':1.5}
# In[15]:
def hook_lsil(plot, element):
# Adds right y-axis
p = plot.handles["plot"]
p.extra_y_scales = {"right": LinearScale()}
p.extra_y_ranges = {"right": Range1d(0,0.55)}
p.add_layout(LinearAxis(y_range_name="right"), "right")
# find the last line and set it to right
lines = [p for p in p.renderers if isinstance(p, GlyphRenderer)]
lines[-1].y_range_name = "right"
def hook_ascus(plot, element):
# Adds right y-axis
p = plot.handles["plot"]
p.extra_y_scales = {"right": LinearScale()}
p.extra_y_ranges = {"right": Range1d(0,0.045)}
p.add_layout(LinearAxis(y_range_name="right"), "right")
# find the last line and set it to right
lines = [p for p in p.renderers if isinstance(p, GlyphRenderer)]
lines[-1].y_range_name = "right"
def hook_abnorm(plot, element):
# Adds right y-axis
p = plot.handles["plot"]
p.extra_y_scales = {"right": LinearScale()}
p.extra_y_ranges = {"right": Range1d(0,0.3)}
p.add_layout(LinearAxis(y_range_name="right"), "right")
# find the last line and set it to right
lines = [p for p in p.renderers if isinstance(p, GlyphRenderer)]
lines[-1].y_range_name = "right"
# In[16]:
def add_norm_curves(column_changer,year_slider):
if column_changer == 'Referral Rate(%)' or column_changer == 'hrHPV/ASC-US rate (%)' or column_changer == 'hrHPV Prevalence (%)':
return None
else:
mu= med_ref_distribution.iloc[-2][column_changer]
variance = med_ref_distribution.iloc[-1][column_changer]**2
sigma = np.sqrt(variance)
x = np.linspace(mu - 3 * np.sqrt(variance), mu + 3 * np.sqrt(variance), 32)
distrib_df = pd.DataFrame({'x': x})
distrib_df['Reference Distribution'] = stats.norm.pdf(x, mu, sigma)
curve_plot = distrib_df.hvplot.line(x='x', xlabel =f'{column_changer}', ylabel='Probability Density', title=f'Reference Curve vs Laboratory Histogram', color='black', hover=False).opts(fontsize = pl_title,fontscale = f_scale*0.75, shared_axes=False,toolbar=None, default_tools = [])
if column_changer == 'ASC/LSIL' or column_changer == 'ASC-US/ASC-H ratio(%)':
outliers_df = results_filtered[(results_filtered.year == year_slider)&(results_filtered.people.str.contains('Cyto'))][['ASC/LSIL','ASC-US/ASC-H ratio(%)','people']]
outliers_df['zscore_ref'] = ((outliers_df[column_changer] - mu)/np.sqrt(variance)).map(lambda x: round(x, 3))
tooltips= f"""
<div>
<span style="font-size: {fpx}; font-weight: bold">@people
</span><br>
<span style= "font-size:{fpx}; font-weight: bold; color: #5B91D1">Z Score:
</span>
<span style="font-size: {fpx};">@zscore_ref
</span>
</div>
"""
d = np.linspace(mu - 3 * np.sqrt(variance), mu + 3 * np.sqrt(variance), len(outliers_df))
else:
outliers_df = results_filtered[(results_filtered.year == year_slider)&(results_filtered.people.str.contains('ytolog'))][['Abnormal Rate(%)', 'people']]
outliers_df['zscore_ref'] = ((outliers_df[column_changer] - mu)/np.sqrt(variance)).map(lambda x: round(x, 3))
tooltips= f"""
<div>
<span style="font-size: {fpx}; font-weight: bold">@people
</span><br>
<span style= "font-size:{fpx}; font-weight: bold; color: #5B91D1">Z Score:
</span>
<span style="font-size: {fpx};">@zscore_ref
</span>
</div>
"""
d = np.linspace(mu - 3 * np.sqrt(variance), mu + 3 * np.sqrt(variance), len(outliers_df))
outliers_df['Reference Distribution'] = (outliers_df[column_changer].apply(lambda x_val: stats.norm.pdf(x_val, mu, np.sqrt(variance))))#*10
hover = HoverTool(tooltips=tooltips)
bins = 60
if column_changer=='ASC/LSIL':
outliers_df['Reference Distribution'] *= 2.5
scatter_plot = outliers_df[outliers_df['zscore_ref'] > 2].hvplot.scatter(x=column_changer, y='Reference Distribution', color='red', size=sdot_size , hover_cols=['people','zscore_ref'], tools = [hover]).opts(shared_axes=False,toolbar=None, default_tools = [])
histo_data = results_filtered[(results_filtered.people.str.contains('Cyto'))&(results_filtered.year == year_slider)][[column_changer, 'people']]
histo_plot = histo_data.hvplot.hist(y=column_changer, bins=bins, ylabel='Frequency',color = '#5B91D1', alpha=0.7,hover=False).opts(shared_axes=False,toolbar=None, default_tools = [])
overlay_plot = (histo_plot*curve_plot.opts(hooks=[hook_lsil])*scatter_plot)
elif column_changer=='ASC-US/ASC-H ratio(%)':
outliers_df['Reference Distribution'] *= 10
scatter_plot = outliers_df[outliers_df['zscore_ref'] < -2].hvplot.scatter(x=column_changer, y='Reference Distribution', color='red', size=sdot_size, hover_cols=['people','zscore_ref'], tools = [hover]).opts(shared_axes=False,toolbar=None, default_tools = [])
histo_data = results_filtered[(results_filtered.people.str.contains('Cyto'))&(results_filtered.year == year_slider)][[column_changer, 'people']]
histo_plot = histo_data.hvplot.hist(y=column_changer, bins=bins, ylabel='Frequency', color = '#5B91D1', alpha=0.7,hover=False).opts(shared_axes=False,toolbar=None, default_tools = [])
overlay_plot = (histo_plot*curve_plot.opts(hooks=[hook_ascus])*scatter_plot)
elif column_changer=='Abnormal Rate(%)':
outliers_df['Reference Distribution']
scatter_plot = outliers_df[outliers_df['zscore_ref'] < -2].hvplot.scatter(x=column_changer, y='Reference Distribution', color='red', size=sdot_size ,height= 800, hover_cols=['people','zscore_ref',], tools = [hover]).opts(shared_axes=False,toolbar=None, default_tools = [])
histo_data = results_filtered[(results_filtered.people.str.contains('Cytot'))&(results_filtered.year == year_slider)][[column_changer, 'people']]
histo_plot = histo_data.hvplot.hist(y=column_changer, bins=bins, ylabel='Frequency',color = '#5B91D1', alpha=0.7, hover=False).opts(shared_axes=False,toolbar=None, default_tools = [])
overlay_plot = (histo_plot*curve_plot.opts(hooks=[hook_abnorm])*scatter_plot)
return overlay_plot.opts(width = 820, height = 400)
interact_norm_curve = pn.bind(add_norm_curves, column_changer,year_slider)
# In[17]:
lab_performance= pn.Tabs(('Performance results',pn.Column(pn.Row(column_changer, year_slider), interactive_no_lab,\
pn.Row(pn.Column(pn.pane.Markdown("<br><br><br>"),vertical_legend_indiv), interactive_func,interact_norm_curve),\
modal_btn)),('Results overview', \
pn.Column(column_changer, inter_labres)),styles = {'font-size': '20px'})
# In[18]:
tabs = pn.Tabs(('Introduction', pn.pane.Markdown("""Welcome to the Dashboards!""")),
('Laboratory management', lab_performance),
('Cytotechnologist', ct_performance),
('Cytopathologist',cp_performance), sizing_mode = 'stretch_both', styles = {'font-size':'20px'})
# In[ ]:
# In[20]:
#Layout using Template
template = pn.template.FastListTemplate(
title='Interactive Quality Assurance Dashboards',
main=[tabs],
modal = modal,
accent_base_color="#0072B5",
header_background="#787878", theme_toggle = False,
)
#start_scheduler()
template.servable()