Reputation: 294
I'm trying to make a dive table that has some numbers that aren't in a pattern that I can see so I have to manually add all the values, but I need to grab the input and round it to the nearest number in the dictionary.
I'll need to convert the input back to string for the output to be correct:
CODE:
class DepthTable:
def __init__(self):
self.d35 = {"10": "A",
"19": "B",
"25": "C",
"29": "D",
"32": "E",
"36": "F",
}
def getpressureGroup(self, depth, time):
if depth == "35":
output = self.d35[time]
else:
output = "No info for that depth"
print(output)
if __name__ == "__main__":
depthtable = DepthTable()
print("Please enter Depth (Use numbers!)")
depth = input()
print("Please Enter time!")
time = input()
depthtable.getpressureGroup(depth,time)
So when the "player" inputs the number 15 for time, I need to round it UP to 19 (Always up even if it's 13 or something like that.) I don't see how I can do this with round() or I might have to make a function that checks EVERY number..
Upvotes: 4
Views: 531
Reputation: 14559
While bisect
and pandas.cut
(as mentioned in other answers) will work, you could do it with just vanilla Python (no imported modules) by looping through the cutoff values. (This is the approach in @Harvey's answer, just presented as a program rather than an interactive session.)
d35 = {
10: "A",
19: "B",
25: "C",
29: "D",
32: "E",
36: "F",
}
def pressure_group(time):
for cutoff in sorted(d35.items()):
if cutoff[0] >= time:
return cutoff[1]
return None # or pick something else for "too high!"
This is a little more verbose than using the modules mentioned, but perhaps easier to follow and understand what's going on, because you're doing it all yourself (which I think is often a good idea, especially if you are trying to learn programming).
Each time through the loop, cutoff
is a pair from the d35
dictionary. Since the pairs are sorted from lowest to highest, you can just stop at the first one which is greater or equal to the input. If the loop finishes (because the input is higher than the highest cutoff), I chose to return None
, but you could return some other value, or raise an exception.
Upvotes: 1
Reputation: 3775
For large lists use standard bisect module or any other dichotomy package. Check the friendly python docs for excact instructions how to solve your task with bisect
https://docs.python.org/2/library/bisect.html#other-examples
If you have numpy, try digitize, seems easier than pandas cut
Python: Checking to which bin a value belongs
Yet for such short list, I would just use simple branching
if 1<x<=18:
...
elif 18<x<=28:
...
elif
Or, for greater speed build case by case array or a dictionary { "1":"19", "2":"19" ... "20": "25" ...} programmatically,
Say with an activestate dictionary inversion snippet
http://code.activestate.com/recipes/415100-invert-a-dictionary-where-values-are-lists-one-lin/
def invert(d):
return dict( (v,k) for k in d for v in d[k] )
d35 = {"10": "A",
"19": "B",
"25": "C",
"29": "D",
"32": "E",
"36": "F",
}
dlist = d35.keys().sort()
d1 = {}
low = -1
for n in dlist[1:]:
up = int(n) + 1
interval = range(low, up)
low = up
d1[ dlist[i] ] = map(str, interval)
result = invert(d1)
Upvotes: 1
Reputation: 5821
Convert the d35
dictionary to a sorted list and step through it:
In [4]: d35 = {"10": "A",
...: "19": "B",
...: "25": "C",
...: "29": "D",
...: "32": "E",
...: "36": "F",
...: }
In [5]: sorted(d35.items())
Out[5]: [('10', 'A'), ('19', 'B'), ('25', 'C'), ('29', 'D'), ('32', 'E'), ('36', 'F')]
In [7]: time = 15
In [11]: for max_time, group_name in sorted(d35.items()):
...: if int(max_time) >= time:
...: break
...:
In [12]: max_time
Out[12]: '19'
In [13]: group_name
Out[13]: 'B'
Modifying your method gives this. I added an else to the for loop to handle times not covered by any group.
def getpressureGroup(self, depth, time):
if depth == "35":
for max_time, group_name in sorted(self.d35.items()):
if int(max_time) >= time:
output = group_name
break
else:
output = "No info for that depth"
else:
output = "No info for that depth"
print(output)
Upvotes: 2
Reputation: 8057
Using your idea of a "function that checks EVERY number", an instance variable keys
can be used to get the key if it exists, or the next highest key:
class DepthTable:
def __init__(self):
self.d35 = {10: "A",
19: "B",
25: "C",
29: "D",
32: "E",
36: "F",
}
self.keys = self.d35.keys()
def getpressureGroup(self, depth, time):
if depth == 35:
rtime = min([x for x in self.keys if x >= time]) # if exists get key, else get next largest
output = self.d35[rtime]
else:
output = "No info for that depth"
print(output)
if __name__ == "__main__":
depthtable = DepthTable()
print("Please enter Depth (Use numbers!)")
depth = int(input())
print("Please Enter time!")
time = int(input())
depthtable.getpressureGroup(depth,time)
Demo:
Please enter Depth (Use numbers!)
35
Please Enter time!
13
B
Please enter Depth (Use numbers!)
35
Please Enter time!
19
B
Please enter Depth (Use numbers!)
35
Please Enter time!
10
A
Upvotes: 2
Reputation: 24945
You can try using cut
from the pandas
module.
More or less, it is made to separate continuous variables into discrete categories, like depths into pressure groups.
You need to specify an array of bins to cut your data into, which you can then label.
So, as an example:
import pandas as pd
import numpy as np
timestocut = [0, 4, 8, 12, 16, 20, 24, 28, 32, 36]
pd.cut(timestocut, bins = np.array([-1,10,19,25,29,32, np.inf]), labels = np.array(['A','B','C','D','E','F']), right = True)
giving:
[A, A, A, B, B, C, C, D, E, F]
Categories (6, object): [A < B < C < D < E < F]
You can see the bins has -1, so we include 0, and np.inf
to catch anything up to infinite.
Integrating this into your code is up to you - personally I would remove the dict and use this mapping.
Upvotes: 1