4

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..

Max Bethke
  • 286
  • 1
  • 2
  • 18
  • why not under the if loop and under the variable output, use an if to check if the number is greater than some number and less than some other number so the case for fifteen would be greater than 10 or less than 19 if those conditions are met change time to equal 19? Does that make sense? – GoldenWest Apr 13 '17 at 17:41
  • Well I mean that would make it a large code for this kind of stuff, the actual dive table has A-Z each with 20 time stamps.. – Max Bethke Apr 13 '17 at 17:43
  • So the problem is that you need to drive each number up to the smallest value at least as large as that number? – Prune Apr 13 '17 at 17:51
  • I don't understand you very well, but I basically need to have a safeguard, since it's diving you want to be safe so we round up the numbers instead of down in usual math like 11 will go up to 19 not 10 but 9 goes to 10. – Max Bethke Apr 13 '17 at 17:56
  • 1
    How about using `cut` from pandas: `import pandas as pd` then :`pd.cut([time], bins = np.array([0,10,19,25,29,32, np.inf]), labels = np.array(['A','B','C','D','E','F']), right = True)` – jeremycg Apr 13 '17 at 18:01
  • Can you post that as a separate answer with some explanation please? I'd like to see if it would work :D – Max Bethke Apr 13 '17 at 18:04

5 Answers5

2

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)
Harvey
  • 5,703
  • 1
  • 32
  • 41
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
  • 1
    Nice. And since you're not using `self.keys` anywhere else, you could really just skip keeping around a variable for it, and use `self.d35.keys()` directly in the comprehension. Speaking of which, you could also do away with the comprehension and just have the "naked" generator expression. Anyway, those are just nits. This is about as quick and direct a solution to OP's problem as it gets. – John Y Apr 13 '17 at 20:37
  • 1
    great observations @JohnY, thank you for your feedback – chickity china chinese chicken Apr 13 '17 at 21:27
1

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)
Community
  • 1
  • 1
Serge
  • 3,387
  • 3
  • 16
  • 34
  • Yikes! I'll still keep this answer open to see if anyone has a better answer than brute forcing this, but the chances don't seem too great. – Max Bethke Apr 13 '17 at 17:48
  • 1
    I mean you are checking the range of where a number exists, That's the logic in what I'm seeing. I don't see how else you could do it without checking x1 and x2 – GoldenWest Apr 13 '17 at 18:04
  • I see, it seems like there might some kind of library like what @jermycg said – Max Bethke Apr 13 '17 at 18:07
1

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.

jeremycg
  • 24,657
  • 5
  • 63
  • 74
  • Sorry, but I still don't really understand... I'm new to python and not that good with seeing what the code does without running it. – Max Bethke Apr 13 '17 at 18:12
  • 1
    so take a look now, the function pd.cut has turned timestocut (`[0, 4, 8, 12, 16, 20, 24, 28, 32, 36]`) to the output (`[A, A, A, B, B, C, C, D, E, F]`) by binning them, based on the given bins, and them labeling each bin with the given labels. – jeremycg Apr 13 '17 at 18:15
  • So what does `np.array([-1,10,19,25,29,32, np.inf])` do? – Max Bethke Apr 13 '17 at 18:17
  • 1
    that's a numpy array, which gives us the bins. So, anything between -1 and 10 is in bin1, between 10 and 19 is in bin2 etc etc. Then the bins are labelled using the next array, which holds the names. – jeremycg Apr 13 '17 at 18:18
  • okay I think I got it, I'll try it out. EDIT: It can't find `arrays` in `np.arrays` am I just being dumb? – Max Bethke Apr 13 '17 at 18:20
  • np.array, not np.arrays. No S! – jeremycg Apr 13 '17 at 18:50
  • are you doing `import numpy as np`? you might have to install numpy if you dont have it. – jeremycg Apr 13 '17 at 18:59
  • I installed pandas and it said it automatically installed numpy, and I did do `import numpy as np` – Max Bethke Apr 13 '17 at 19:01
1

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.

Community
  • 1
  • 1
John Y
  • 14,123
  • 2
  • 48
  • 72