- numpy - pandas - openpyxl - matplotlib - scipy - panel==0.13.1a2 - paths: - ./zscore_computation.py - ./datetime.py

Z-Score App

This is a WebApp of the Z-Score App available as standalone program for Mac and Windows on GitHub.
It is based on published DXA reference values for children and adults.

How to use:

Loading ...
Measurement DataComputed ValuesLMS-Plots
import asyncio import numpy as np import scipy.stats import panel as pn import pandas as pd import openpyxl from panel.io.pyodide import show from io import StringIO, BytesIO import os import zscore_computation as zc from js import fetch # import datetime import time import math import datetime #### --- code to load and save data files from website --- # import asyncio # from js import fetch # url = "https://raw.githubusercontent.com/FlorianKrach/scientific-LMS-zscore-app/master/data/adults_LMS_FMI_gender0.csv" # response = await fetch(url) # text = await response.text() # filename = "text.csv" # with open(filename, "w") as f: # f.write(text) # df = pd.read_csv(filename, index_col=0) ### --------------------------------------------- #### --- code to load data files from local directory --- # import asyncio # from js import fetch # from io import StringIO # url = "data/adults_LMS_FMI_gender0.csv" # response = await fetch(url) # text = await response.text() # df = pd.read_csv(StringIO(text), index_col=0) ### --------------------------------------------- pyscript.write("output", "Loading data ...") files = ['adults_LMS_appendicular_LMI_gender1.csv', 'adults_LMS_appendicular_LMI_gender0.csv', 'children_LMS_LMI_gender1.csv', 'children_LMS_LMI_gender0.csv', 'children_LMS_fitted_ALMI_gender0.csv', 'children_LMS_fitted_ALMI_gender1.csv', 'adults_LMS_VAT_mass_gender1.csv', 'adults_LMS_VAT_mass_gender0.csv', 'children_LMS_FMI_gender0.csv', 'children_LMS_BMI_gender1.csv', 'children_LMS_BMI_gender0.csv', 'children_LMS_FMI_gender1.csv', 'adults_LMS_LMI_gender1.csv', 'children_LMS_fitted_LMI_gender0.csv', 'children_LMS_fitted_LMI_gender1.csv', 'adults_LMS_LMI_gender0.csv', 'children_LMS_appendicular_LMI_gender0.csv', 'children_LMS_appendicular_LMI_gender1.csv', 'adults_LMS_FM_android_quotient_gynoid_gender1.csv', 'adults_LMS_FM_android_quotient_gynoid_gender0.csv', 'children_LMS_weight_gender1.csv', 'children_LMS_FM_trunk_quotient_limb_gender1.csv', 'adults_LMS_FM_trunk_quotient_limb_gender0.csv', 'adults_LMS_FM_trunk_quotient_limb_gender1.csv', 'children_LMS_FM_trunk_quotient_limb_gender0.csv', 'children_LMS_weight_gender0.csv', 'children_LMS_height_gender1.csv', 'children_LMS_height_gender0.csv', 'children_LMS_percent_FM_gender0.csv', 'children_LMS_percent_FM_gender1.csv', 'children_LMS_fitted_BMI_gender0.csv', 'adults_LMS_FMI_gender0.csv', 'children_LMS_fitted_FMI_gender1.csv', 'children_LMS_fitted_FMI_gender0.csv', 'adults_LMS_FMI_gender1.csv', 'children_LMS_fitted_BMI_gender1.csv'] if not os.path.exists("data1/"): os.makedirs("data1/") for filename in files: url = "data/{}".format(filename) response = await fetch(url) text = await response.text() savefile = "data1/{}".format(filename) with open(savefile, "w") as f: f.write(text) pyscript.write("output", "") uploadButton = pn.widgets.Button(name='Compute', button_type = 'primary') textInput_dict = {} params = ['Birthday', 'Measurement-day', 'Gender', 'LMI', 'FMI', 'FM_android_quotient_gynoid', 'FM_trunk_quotient_limb', 'appendicular_LMI', 'VAT_mass', 'BMI', 'height', 'weight', 'fitted_BMI', 'fitted_FMI', 'fitted_LMI', 'fitted_ALMI'] descs = ['Birthday *', 'Measurement Day *', 'Gender *', 'LMI (kg/m^2)', 'FMI (kg/m^2)', 'FM android / gynoid', 'FM trunk / limb', 'appendicular LMI', 'VAT mass (g)', 'BMI (kg/m^2)', 'height (cm)', 'weight (kg)', 'fitted BMI (kg/m^x)', 'fitted FMI (kg/m^x)', 'fitted LMI (kg/m^x)', 'fitted ALMI (kg/m^x)'] min_age = 6.0 child_adult_split = 18.0 max_age = 82.0 male = 0 female = 1 genders = {"male": 0, "female":1} path = 'data1/' filename_children = 'children_LMS_{}_gender{}.csv' filename_adults = 'adults_LMS_{}_gender{}.csv' params_only_children = [ 'BMI', 'height', 'weight', 'fitted_BMI', 'fitted_FMI', 'fitted_LMI', 'fitted_ALMI'] params_only_adults = ['VAT_mass', 'FM_android_quotient_gynoid'] def zscore_to_perc(zscore): return scipy.stats.norm.cdf(zscore) def nice_name(string): string = string.replace('_', ' ').replace('quotient', '/') return string # get all input fields for p, desc in zip(params, descs): if p in ['Birthday', 'Measurement-day']: textInput_dict[p] = pn.widgets.DatePicker(name="{}".format(desc)) elif p in ["Gender"]: textInput_dict[p] = pn.widgets.Select(name=desc, options=genders) else: textInput_dict[p] = pn.widgets.TextInput( name="", placeholder=desc) def process_file(event): pyscript.write("computing-info", 'computing...') values_dict = {} birthday = None meas_day = None for p in params: val = textInput_dict[p].value if p not in ['Gender', 'Birthday', 'Measurement-day']: if val == '': values_dict[p] = None pyscript.write("output-{}".format(p), '') else: try: val = float(val) if val > 0: values_dict[p] = val pyscript.write("output-{}".format(p), '') else: values_dict[p] = None pyscript.write( "output-{}".format(p), 'invalid entry! entry > 0 is needed') except Exception: values_dict[p] = None pyscript.write( "output-{}".format(p), 'invalid entry! a number > 0 is needed (use . for comma)') elif p == 'Birthday': values_dict[p] = None try: birthday = datetime.datetime.strptime(str(val), "%Y-%m-%d") pyscript.write("output-{}".format(p), '') except Exception: birthday = None values_dict['Age'] = None pyscript.write( "output-{}".format(p), 'invalid Birthday!') elif p == 'Measurement-day': values_dict[p] = None try: meas_day = datetime.datetime.strptime(str(val), "%Y-%m-%d") pyscript.write("output-{}".format(p), '') except Exception: meas_day = None values_dict['Age'] = None pyscript.write( "output-{}".format(p), 'invalid Measurement-day!') elif p == 'Gender': values_dict[p] = val # compute age if birthday is not None and meas_day is not None: age = (meas_day - birthday).days/365.25 pyscript.write("output-Age", 'age: {:.2f}'.format(age)) if min_age <= age <= max_age: values_dict['Age'] = age else: values_dict['Age'] = None pyscript.write( "output-Age", 'invalid age ({:.2f}) -- needs age between {} - {}'.format( age, min_age, max_age)) # compute z-scores if (values_dict['Age'] is not None) and (values_dict['Gender'] is not None): if values_dict['Age'] < child_adult_split: filename = filename_children params_avail = list(set(params) - set(params_only_adults)) params_unavail = params_only_adults else: filename = filename_adults params_avail = list(set(params) - set(params_only_children)) params_unavail = params_only_children gender = values_dict['Gender'] age = values_dict['Age'] values_dict_ = {key: values_dict[key] for key in params_avail} out_values = zc.compute_zscores_all_params( path=path, filename=filename, gender=gender, age=age, value_dict=values_dict_) for p in params_unavail: if values_dict['Age'] < child_adult_split: pyscript.write("output-{}".format(p), '-> only available for adults') else: pyscript.write("output-{}".format(p), '-> only available for children') for p in params_avail: if p not in ['Gender', 'Birthday', 'Measurement-day']: # pyscript.write("output", "{} - {}".format(p, out_values[p]), # append=True) if out_values[p] is not None: if type(out_values[p][0]) == str: pyscript.write("output-{}".format(p), '{}'.format(out_values[p][0])) else: if out_values[p][0] is not None: pyscript.write( "output-{}".format(p), '{}:\nz-Score: {:.2f}, percentile: {:.2f}%'.format( nice_name(p), out_values[p][0], zscore_to_perc(out_values[p][0])*100)) # show plot f = out_values[p][1] ax = f.get_axes()[0] g_str = 'male' if gender == male else 'female' ca_str = 'children' if age < child_adult_split else 'adults' ax.set_title('{} -- {} -- {}'.format(nice_name(p), ca_str, g_str)) if age < child_adult_split: ax.set_xlim([None, child_adult_split+1]) else: ax.set_xlim([child_adult_split-3, max_age+5]) ax.set_xlabel("age") pyscript.write("plot-{}".format(p), f, append=False) for p in params_unavail: pyscript.write("plot-{}".format(p), "", append=False) pyscript.write("computing-info", '') uploadButton.on_click(process_file) await show(uploadButton, 'upload') # show all input fields for p in params: await show(textInput_dict[p], 'fileinput-{}'.format(p))