Max Bethke
Max Bethke

Reputation: 294

Rounding to specific numbers in Python 3.6

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

Answers (5)

John Y
John Y

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

Serge
Serge

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

Harvey
Harvey

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

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

jeremycg
jeremycg

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

Related Questions