Is it possible to use variables values in if statements in Python? - python

I have a database table about people. They have variables like age, height, weight etc..
I also have another database table about charasteristics of the people. This has three fields:
Id: Just a running number
Condition: For example "Person is teenager" or "Person is overweight"
Formula: For example for the "Person is teenager" the formula is "age > 12 and age < 20" or for the overweight "weight / height * height > 30"
There are more than 50 conditions like there. When I want to define the characteristics of the person I would need to make if statement for all these conditions which makes the code quite messy and also hard to maintain (when ever I add a new condition to database I also need to add a new if statement in the code)
If I type the formulas directly to database is it possible to use those as if statements directly? As in if(print(characteristic['formula']) etc..
What I am looking is something like this, I am using Python.
In this code
Person is one person already fetched from database as a dict
Characteristics are all the characteristics fetched from the database as a list of dictionaries
def getPeronCharacteristics(person, characteristics):
age = person['age']
weight = person['weight'] etc...
personsCharacteristics = []
for x in characteristics:
if(x['formula']):
personCharacteristics.append(x['condition'])
return personCharacteristics
Now in this part if(x['formula']) instead of checking if the variable is true it should "print" the variable value and run if statement agains that e.g. if(age > 12 and age < 20):
Is this possible in some way? Again the whole point of this is that when I come up with new conditions I could just add a new row to the database without altering any code and adding yet another if statement.

Do you mean like this?
#
#Example file for working with conditional statement
#
def main():
x,y =2,8
if(x < y):
st= "x is less than y"
print(st)
if __name__ == "__main__":
main()

This is possible using python's eval function:
if eval(x['formula']):
...
However, this is usually discouraged as it can make it complicated to understand your program, and can give security problems if you're not very careful about how your database is accessed and what can end up in there.

Related

How to create a dataframe with a dynamic text parameter passed to a function in Python

(I'm pretty new to Python,(and even to coding)forgive me for my stupidity.)
I'm trying to pass a text value and a list as parameters to a function. Here's an example :
Names = File['Student_Name']
Scores = File['Marks']
for a in range(0,100):
Student_Name = [Names[a]]
Marks = []
NewDf = pd.DataFrame(PreCovid(Student_Name,Marks))
Master_Sheet_PreCovid = NewDf
Master_Sheet_PreCovid
What I wish to achieve is passing Name of a Student, as a string, one at a time, to the function. In this code, I'm vaguely creating a df with each loop iteration, which obviously will only return me the last value, however, I wish to get the output for complete list of Students. What modifications/additions do I make in this code to make it work.
I followed this thread, Why the function is only returning the last value? , which was similar to my query, however might not work with my requirements.
Edited : I actually have 2 sheets that I'm fetching my data from,one is a Main Sheet,that has all the data with redundancy,I've a Rule book with unique values and the rules for calculation.In this code I'm only fetching values from Rule Book,then going to the function,fetching data based on these values from Main Sheet,performing my calculations,creating a new dataframe,inserting the values I get here into that dataframe as well,and return the Final dataframe.Right now, the calculation tested based only on Student_Name has worked, but now I've a bigger problem of calculating also based on Marks.
At the risk of sounding arrogant, I only wish to pass the name as string, not as list.
Again, I'm sorry about the stupidity of my query.
Give it a try:
Names = File['Student_Name']
Scores = File['Marks']
Master_Sheet_PreCovid = []
for a in range(0,100):
Student_Name = [Names[a]]
Marks = []
NewDf = pd.DataFrame(PreCovid(Student_Name,Marks))
Master_Sheet_PreCovid.append(NewDf)
Master_Sheet_PreCovid = pd.concat(Master_Sheet_PreCovid)
print(Master_Sheet_PreCovid)

Fixing a meeting room function schedule with double and triple bookings to determine space usage

I need to calculate the total amount of time each group uses a meeting space. But the data set has double and triple booking, so I think I need to fix the data first. Disclosure: My coding experience consists solely of working through a few Dataquest courses, and this is my first stackoverflow posting, so I apologize for errors and transgressions.
Each line of the data set contains the group ID and a start and end time. It also includes the booking type, ie. reserved, meeting, etc. Generally, the staff reserve a space for the entire period, which would create a single line, and then add multiple lines for each individual function when the details are known. They should segment the original reserved line so it's only holding space in between functions, but instead they double book the space, so I need to add multiple lines for these interim RES holds, based on the actual holds.
Here's what the data basically looks like:
Existing data:
functions = [['Function', 'Group', 'FunctionType', 'StartTime', 'EndTime'],
[01,01,'RES',2019/10/04 07:00,2019/10/06 17:00],
[02,01,'MTG',2019/10/05 09:00,2019/10/05 12:00],
[03,01,'LUN',2019/10/05 12:30,2019/10/05 13:30],
[04,01,'MTG',2019/10/05 14:00,2019/10/05 17:00],
[05,01,'MTG',2019/10/06 09:00,2019/10/06 12:00]]
I've tried to iterate using a for loop:
for index, row in enumerate(functions):
last_row_index = len(functions) - 1
if index == last_row_index:
pass
else:
current_index = index
next_index = index + 1
if row[3] <= functions[next_index][2]:
next
elif row[4] == 'RES' or row[6] < functions[next_index][6]:
copied_current_row = row.copy()
row[3] = functions[next_index][2]
copied_current_row[2] = functions[next_index][3]
functions.append(copied_current_row)
There seems to be a logical problem in here, because that last append line seems to put the program into some kind of loop and I have to manually interrupt it. So I'm sure it's obvious to someone experienced, but I'm pretty new.
The reason I've done the comparison to see if a function is RES is that reserved should be subordinate to actual functions. But sometimes there are overlaps between actual functions, so I'll need to create another comparison to decide which one takes precedence, but this is where I'm starting.
How I (think) I want it to end up:
[['Function', 'Group', 'FunctionType', 'StartTime', 'EndTime'],
[01,01,'RES',2019/10/04 07:00,2019/10/05 09:00],
[02,01,'MTG',2019/10/05 09:00,2019/10/05 12:00],
[01,01,'RES',2019/10/05 12:00,2019/10/05 12:30],
[03,01,'LUN',2019/10/05 12:30,2019/10/05 13:30],
[01,01,'RES',2019/10/05 13:30,2019/10/05 14:00],
[04,01,'MTG',2019/10/05 14:00,2019/10/05 17:00],
[01,01,'RES',2019/10/05 14:00,2019/10/06 09:00],
[05,01,'MTG',2019/10/06 09:00,2019/10/06 12:00],
[01,01,'RES',2019/10/06 12:00,2019/10/06 17:00]]
This way, I could do a simple calculation of elapsed time for each function line and add it up to see how much time they had the space booked for.
What I'm looking for here is just some direction I should pursue, and I'm definitely not expecting anyone to do the work for me. For example, am I on the right path here, or would it be better to use pandas and vectorized functions? If I can get the basic direction right, I think I can muddle through the specifics.
Thank-you very much,
AF

Can you use if statements to create variables?

I am trying to make the switch from STATA to python for data analysis and I'm running into some hiccups that I'd like some help with. I am attempting to create a secondary variable based on some values in an original variable. I want to create a binary variable which identifies fall accidents (E-codes E880.xx -E888.xx) with a value of 1, and all other e-codes with a value of 0. in a list of ICD-9 codes with over 10,000 rows, so manual imputation isn't possible.
in STATA the code would look something like this
newvar= 0
replace newvar = 1 if ecode_variable == "E880"
replace newvar = 1 if ecode_variable == "E881"
etc
I tried a similar statement in python, but it's not working
data['ecode_fall'] = 1 if data['ecode'] == 'E880'
is this type of work possible in python? Is there a function in the numpy or pandas packages that could help with this.
I've also tried creating a dictionary variable which calls the fall injury codes 1 and applying it to the variable to no avail.
Put the if first.
if data['ecode'] == 'E880': data['ecode_fall'] = 1
you can break it out into two lines like this:
if data['ecode'] == 'E880':
data['ecode_fall'] = 1
or if you include an else statement you can have it in one line, similar syntax to your SATA code:
data['ecode_fall'] = 1 if data['ecode'] == 'E880' else None
Following from the other answers, you can also check multiple values at once like so:
if data['ecode'] in ('E880', 'E881', ...):
data['ecode_fall'] = 1
this leaves you having to only do one if statement per unique value of data['ecode_fall'].

Certain variables assigned by functions are not being assigned in python script

I am working on procedural generation of character concepts. As part of this, I have set variables to change the pronouns in text so they will be correct for the generated character. However, for some reason, my code refuses to assign variables for this, resulting in output like "Bob is a Caucasian . works as..." It should read "Bob is a Caucasian male. He works as..."
Everywhere a pronoun or the word male or female should be, it prints nothing. Not to console, nor to file. When I strip the entire script down to just the code that calls for the function to run, and the function itself, it still wont assign the variables.
Other systems that use functions to assign variables are working 100% fine.
I've rewritten new functions 3 times to try different approaches to this. I've tried making the variables global. I don't know what else to try.
Here is the function.
def pronouns(sex1):
pronoun1, pronoun1alternate, pronoun2, pronoun2alternate = "", "", "", ""
if sex1 == "male":
pronoun1 = "He"
pronoun1alternate = "he"
pronoun2 = "His"
pronoun2alternate = "his"
elif sex1 == "female":
pronoun1 = "She"
pronoun1alternate = "she"
pronoun2 = "Her"
pronoun2alternate = "her"
return pronoun1, pronoun1alternate, pronoun2, pronoun2alternate
The variable sex1 is created much later on, and cannot be created within this function as I may want to expand this program for fantasy and sci-fi character concepts later on and decided to handle sex selection with each individual race in case I want to do something like Species-8472 in the future.
Here is how the function called later on.
p1, p1a, p2, p2a = pronouns(sex1)
I have been informed that python passes the values but not names. It didn't work when the function outputted p1 p1a and so on either. I was told using different names in the same order might fix the problem. It did not.
Here his how the variables are supposed to be used.
description = name + " is a " + race + " " + sex + ". " + p1 + " has " + eyes + " eyes. " + colorapp
It has no problem filling out the name, race, eye color, and the colorapp variable, but cannot ever, regardless of what I do, fill out sex, or any pronoun values.
No error messages occur when this code is run in isolation. It will print blank lines if asked to just print the pronouns or sex. However, sex1 will print successfully, as will almost every other variable in the program.
How are you assigning value to variable sex1? Make sure it is passed in lowercase. If this is the problem you can use sex1.lower() on lines 3 and 8 of the function.

Python: creating a dictionary that writes high scores to a file

First: you don't have to code this for me, unless you're a super awesome nice guy. But since you're all great at programming and understand it so much better than me and all, it might just be easier (since it's probably not too many lines of code) than writing paragraph after paragraph trying to make me understand it.
So - I need to make a list of high scores that updates itself upon new entries. So here it goes:
First step - done
I have player-entered input, which has been taken as a data for a few calculations:
import time
import datetime
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
a = raw_input("Enter weight: ")
b = raw_input("Enter height: ")
c = a/b
Second step - making high score list
Here, I would need some sort of a dictionary or a thing that would read the previous entries and check if the score (c) is (at least) better than the score of the last one in "high scores", and if it is, it would prompt you to enter your name.
After you entered your name, it would post your name, your a, b, c, and time in a high score list.
This is what I came up with, and it definitely doesn't work:
list = [("CPU", 200, 100, 2, time1)]
player = "CPU"
a = 200
b = 100
c = 2
time1 = "20.12.2012, 21:38"
list.append((player, a, b, c, time1))
list.sort()
import pickle
scores = open("scores", "w")
pickle.dump(list[-5:], scores)
scores.close()
scores = open("scores", "r")
oldscores = pickle.load(scores)
scores.close()
print oldscores()
I know I did something terribly stupid, but anyways, thanks for reading this and I hope you can help me out with this one. :-)
First, don't use list as a variable name. It shadows the built-in list object. Second, avoid using just plain date strings, since it is much easier to work with datetime objects, which support proper comparisons and easy conversions.
Here is a full example of your code, with individual functions to help divide up the steps. I am trying not to use any more advanced modules or functionality, since you are obviously just learning:
import os
import datetime
import cPickle
# just a constants we can use to define our score file location
SCORES_FILE = "scores.pickle"
def get_user_data():
time1 = datetime.datetime.now()
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
a = None
while True:
a = raw_input("Enter weight: ")
try:
a = float(a)
except:
continue
else:
break
b = None
while True:
b = raw_input("Enter height: ")
try:
b = float(b)
except:
continue
else:
break
c = a/b
return ['', a, b, c, time1]
def read_high_scores():
# initialize an empty score file if it does
# not exist already, and return an empty list
if not os.path.isfile(SCORES_FILE):
write_high_scores([])
return []
with open(SCORES_FILE, 'r') as f:
scores = cPickle.load(f)
return scores
def write_high_scores(scores):
with open(SCORES_FILE, 'w') as f:
cPickle.dump(scores, f)
def update_scores(newScore, highScores):
# reuse an anonymous function for looking
# up the `c` (4th item) score from the object
key = lambda item: item[3]
# make a local copy of the scores
highScores = highScores[:]
lowest = None
if highScores:
lowest = min(highScores, key=key)
# only add the new score if the high scores
# are empty, or it beats the lowest one
if lowest is None or (newScore[3] > lowest[3]):
newScore[0] = raw_input("Enter name: ")
highScores.append(newScore)
# take only the highest 5 scores and return them
highScores.sort(key=key, reverse=True)
return highScores[:5]
def print_high_scores(scores):
# loop over scores using enumerate to also
# get an int counter for printing
for i, score in enumerate(scores):
name, a, b, c, time1 = score
# #1 50.0 jdi (20.12.2012, 15:02)
print "#%d\t%s\t%s\t(%s)" % \
(i+1, c, name, time1.strftime("%d.%m.%Y, %H:%M"))
def main():
score = get_user_data()
highScores = read_high_scores()
highScores = update_scores(score, highScores)
write_high_scores(highScores)
print_high_scores(highScores)
if __name__ == "__main__":
main()
What it does now is only add new scores if there were no high scores or it beats the lowest. You could modify it to always add a new score if there are less than 5 previous scores, instead of requiring it to beat the lowest one. And then just perform the lowest check after the size of highscores >= 5
The first thing I noticed is that you did not tell list.sort() that the sorting should be based on the last element of each entry. By default, list.sort() will use Python's default sorting order, which will sort entries based on the first element of each entry (i.e. the name), then mode on to the second element, the third element and so on. So, you have to tell list.sort() which item to use for sorting:
from operator import itemgetter
[...]
list.sort(key=itemgetter(3))
This will sort entries based on the item with index 3 in each tuple, i.e. the fourth item.
Also, print oldscores() will definitely not work since oldscores is not a function, hence you cannot call it with the () operator. print oldscores is probably better.
Here are the things I notice.
These lines seem to be in the wrong order:
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
When the user enters the height and weight, they are going to be read in as strings, not integers, so you will get a TypeError on this line:
c = a/b
You could solve this by casting a and b to float like so:
a = float(raw_input("Enter weight: "))
But you'll probably need to wrap this in a try/catch block, in case the user puts in garbage, basically anything that can't be cast to a float. Put the whole thing in a while block until they get it right.
So, something like this:
b = None
while b == None:
try:
b = float(raw_input("Enter height: "))
except:
print "Weight should be entered using only digits, like '187'"
So, on to the second part, you shouldn't use list as a variable name, since it's a builtin, I'll use high_scores.
# Add one default entry to the list
high_scores = [("CPU", 200, 100, 2, "20.12.2012, 4:20")]
You say you want to check the player score against the high score, to see if it's best, but if that's the case, why a list? Why not just a single entry? Anyhow, that's confusing me, not sure if you really want a high score list, or just one high score.
So, let's just add the score, no matter what:
Assume you've gotten their name into the name variable.
high_score.append((name, a, b, c, time1))
Then apply the other answer from #Tamás
You definitely don't want a dictionary here. The whole point of a dictionary is to be able to map keys to values, without any sorting. What you want is a sorted list. And you've already got that.
Well, as Tamás points out, you've actually got a list sorted by the player name, not the score. On top of that, you want to sort in downward order, not upward. You could use the decorate-sort-undecorate pattern, or a key function, or whatever, but you need to do something. Also, you've put it in a variable named list, which is a very bad idea, because that's already the name of the list type.
Anyway, you can find out whether to add something into a sorted list, and where to insert it if so, using the bisect module in the standard library. But it's probably simpler to just use something like SortedCollection or blist.
Here's an example:
highscores = SortedCollection(scores, key=lambda x: -x[3])
Now, when you finish the game:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[-1]
That's it. If you were actually not in the top 10, you'll be added at #11, then removed. If you were in the top 10, you'll be added, and the old #10 will now be #11 and be removed.
If you don't want to prepopulate the list with 10 fake scores the way old arcade games used to, just change it to this:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[10:]
Now, if there were already 10 scores, when you get added, #11 will get deleted, but if there were only 3, nothing gets deleted, and now there are 4.
Meanwhile, I'm not sure why you're writing the new scores out to a pickle file, and then reading the same thing back in. You probably want to do the reading before adding the highscore to the list, and then do the writing after adding it.
You also asked how to "beautify the list". Well, there are three sides to that.
First of all, in the code, (player, a, b, c, time1) isn't very meaningful. Giving the variables better names would help, of course, but ultimately you still come down to the fact that when accessing list, you have to do entry[3] to get the score or entry[4] to get the time.
There are at least three ways to solve this:
Store a list (or SortedCollection) of dicts instead of tuples. The code gets a bit more verbose, but a lot more readable. You write {'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1}, and then when accessing the list, you do entry['score'] instead of entry[3].
Use a collection of namedtuples. Now you can actually just insert ScoreEntry(player, a, b, c, time1), or you can insert ScoreEntry(player=player, height=a, weight=b, score=c, time=time1), whichever is more readable in a given case, and they both work the same way. And you can access entry.score or as entry[3], again using whichever is more readable.
Write an explicit class for score entries. This is pretty similar to the previous one, but there's more code to write, and you can't do indexed access anymore, but on the plus side you don't have to understand namedtuple.
Second, if you just print the entries, they look like a mess. The way to deal with that is string formatting. Instead of print scores, you do something like this:
print '\n'.join("{}: height {}, weight {}, score {} at {}".format(entry)
for entry in highscores)
If you're using a class or namedtuple instead of just a tuple, you can even format by name instead of by position, making the code much more readable.
Finally, the highscore file itself is an unreadable mess, because pickle is not meant for human consumption. If you want it to be human-readable, you have to pick a format, and write the code to serialize that format. Fortunately, the CSV format is pretty human-readable, and most of the code is already written for you in the csv module. (You may want to look at the DictReader and DictWriter classes, especially if you want to write a header line. Again, there's the tradeoff of a bit more code for a lot more readability.)

Categories