HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Calculate where the patient results reside within the normal distribution

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thedistributionwherenormalwithincalculateresultspatientreside

Problem

My background is medical, but I required a program that calculated whether my patients are "normal" or 1-2 SD values away from the mean. The program works just fine, but I'm interested how I can do better. (This all was made after I started learning python a month ago, so please be kind)

I'm particularly interested in making the standard deviation calculation process a bit more easy to add in and remove or maybe some way to make it more modular overall? (The part with all that mean_list and sd_list stuff)

Currently, I have to manually enter mean and std deviations into lists and make sure the format is always right. On top of that, I have to create individual functions for every test I've got. While this has benefits, like managing the tests with exceptions, different number of results or entirely different result system; most of the code is just repetition of itself. Which is not particularly desired. (Especially I'm near 1700 lines and at least 1/3-1/4 of it is repetition)

```
def csvWriter(patient_admin, patient_ID, patient_age, patient_sex, patient_edu, test_name, printable_list):
import csv
from time import strftime
date = strftime("%Y-%m-%d")
time = strftime("%H:%M:%S")
with open(test_name, 'a', encoding='ISO-8859-9', newline='') as csvfile:
data_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
data = ["", patient_admin, date, time, patient_ID, patient_age, patient_sex, patient_edu] + printable_list
data_writer.writerow(data)
csvfile.close()

def txtWrite(patient_admin, patient_ID, console_results):
from time import strftime
date = strftime("%Y-%m-%d")
file_name = str(patient_ID) + "-(" + date + ")-" + patient_admin + ".txt"
with open(file_name, 'a', encoding='UTF-8') as file:
file.write("\n" + console_results)
file.close()

def txtCreate(patient_admin, patient_ID, patient_age, patient_sex, patient_edu):
#creates a txt file with desir

Solution

Overview

There's a lot of code to cover here.

The good news is that I was able to understand the code reasonably well, despite the length and the Turkish strings. You did a pretty good job of sticking with English for the code, so it just took a little use of Google Translate to figure out the rest.

If I had to pick the three most important issues, they would be:

  • Don't misuse functions as goto labels.



  • Object-oriented programming would be very beneficial here.



  • You need some better input/output routines, especially for formatting output.



Proper use of functions

Execution starts here:

while True:
    mainStartup()


Then, it asks whether you have entered the admin and patient information correctly ("Yukarıdaki bilgileri onaylıyor musunuz? (e)vet/(h)ayır"). If you say no, then it calls mainStartup() recursively. That is inappropriate nesting; it should be done using a loop instead.

If the information is correct, then it calls mainMenu(). Several places within mainMenu() call mainMenu() recursively, which is similarly inappropriate. Even worse, if the user enters choice 15 to exit, it calls mainStartup(). Since it was the mainStartup() function that had called mainMenu() in the first place, you should really be using return to go back out one level instead of doing mutual recursion.

Your testSbst() function, on the other hand, does it right. When it finishes, it returns control to its caller.

Object-oriented programming

You often pass a cluster of parameters patient_ID, patient_age, patient_edu, patient_sex, and patient_admin. It would be simpler to group those pieces of information together in one Patient object. A namedtuple would be perfect for that.

There are other indications for use of object-oriented programming. The SBST test is one of 15 tests. Those test suites presumably all share some common characteristics. That suggests that SBST should be one of 15 subclasses of a Test base class. Each subclass should know, for example, the name of the test and the various metrics and their reference values. The per-patient test scores, on the other hand, could be considered as instance state in a Test object. You can, eventually, move each test out into its own .py file.

Yet another place where OOP would be useful is in constructing the three types of output. It would be more elegant to define three Reporter classes, which are put in an array reporters = [ScreenReporter, TextFileReporter, CSVReporter]. Then, you can just say...

for reporter in reporters:
    reporter.report(test)


... and each reporter will know how to send the appropriately formatted test results to the screen, the text file, and the CSV file.

Input/ouput routines

Modern Python has

  • f-strings, since Python 3.6



  • str.format(), since Python 2.6



  • Template strings, since Python 2.4



  • printf-style formatting



In comparison, manual concatenation is cumbersome:

for i in range(len(std_dev_output_list)):
    console_result.append("Hastanın puanı: " + str(result_list[i]) + ", " + 
    str(std_dev_verbal_list[i]) + ", " + str(std_dev_output_list[i]) + "SD")


You should try to avoid unnecessary string contenation in places like this, too:

print("\n===========================================\n" + 
"Akbulut Standart Sapma Hesaplayıcı v0.4.1" +
"\n===========================================\n")


Consecutive string literals will automatically be treated as one long string. Better yet, use triple-quoted long strings.

Since a significant part of the work involves asking the user for input and validating that input, it would be worthwhile to add some input/output routines. For example, you have this code that implements a text menu:

```
def mainMenu(patient_ID, patient_age, patient_edu, patient_sex, patient_admin, menu_done_tests):
try:
menu_list = [testStroop, testVf, testSf, testSbst, testTm,
testMmt, testCd, testMoca, testEcr, testWisconsin, testWechsler, testRkft, testSdot, testVvt]
#list of tests' function names

test_name_list = ["(1)Stroop ", "(2)Sözel akıcılık ", "(3)Semantik Akıcılık ",
"(4)SBST ", "(5)İz sürme ", "(6)Mini Mental test ", "(7)Saat çizme ",
"(8)MOCA ", "(9)Artırılmış İpuçlu Hatırlama ",
"(10)Yetişkin Wisconsin Kart Eşleme ", "(11)Wechsler Zeka testi ",
"(12)Rey Karmaşık Figür Testi ", "(13)Çizgi yönünü belirleme testi ",
"(14)Visual Verbal Test"]
#verbal names of the tests

menu_ui = ("==================================================\n")
for i in range(len(test_name_list)):
if (i+1)%3 == 0:
menu_ui = menu_ui + test_name_list[i] + "\n"
else:
menu_ui = menu_ui + test_name_list[i]
menu_ui = menu_ui + "\n"
print

Code Snippets

while True:
    mainStartup()
for reporter in reporters:
    reporter.report(test)
for i in range(len(std_dev_output_list)):
    console_result.append("Hastanın puanı: " + str(result_list[i]) + ", " + 
    str(std_dev_verbal_list[i]) + ", " + str(std_dev_output_list[i]) + "SD")
print("\n===========================================\n" + 
"Akbulut Standart Sapma Hesaplayıcı v0.4.1" +
"\n===========================================\n")
def mainMenu(patient_ID, patient_age, patient_edu, patient_sex, patient_admin, menu_done_tests):
    try:
        menu_list = [testStroop, testVf, testSf, testSbst, testTm,
                     testMmt, testCd, testMoca, testEcr, testWisconsin, testWechsler, testRkft, testSdot, testVvt]  
            #list of tests' function names

        test_name_list = ["(1)Stroop ", "(2)Sözel akıcılık ", "(3)Semantik Akıcılık ",
                          "(4)SBST ", "(5)İz sürme ", "(6)Mini Mental test ", "(7)Saat çizme ",
                          "(8)MOCA ", "(9)Artırılmış İpuçlu Hatırlama ", 
                          "(10)Yetişkin Wisconsin Kart Eşleme ", "(11)Wechsler Zeka testi ",
                          "(12)Rey Karmaşık Figür Testi ", "(13)Çizgi yönünü belirleme testi ",
                          "(14)Visual Verbal Test"] 
            #verbal names of the tests

        menu_ui = ("==================================================\n") 
        for i in range(len(test_name_list)):
            if (i+1)%3 == 0:
                menu_ui = menu_ui + test_name_list[i] + "\n"
            else:
                menu_ui = menu_ui + test_name_list[i]                
        menu_ui = menu_ui + "\n"   
        print(menu_ui)
        #creates and prints the mainMenu

        print("Şu ana kadar yapılan testler: ")
        print(menu_done_tests)
        #prints tests that were done so far (could be made into a string, and printed)        

        menu_input = int(input("Girmek istediğiniz testin numarasını giriniz veya çıkış için (" + str(len(menu_list)+1) + ") giriniz: " ))

Context

StackExchange Code Review Q#134570, answer score: 7

Revisions (0)

No revisions yet.