patternpythonMinor
Calculate where the patient results reside within the normal distribution
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
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:
Proper use of functions
Execution starts here:
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
If the information is correct, then it calls
Your
Object-oriented programming
You often pass a cluster of parameters
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
Yet another place where OOP would be useful is in constructing the three types of output. It would be more elegant to define three
... and each
Input/ouput routines
Modern Python has
In comparison, manual concatenation is cumbersome:
You should try to avoid unnecessary string contenation in places like this, too:
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
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"
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.