Python if-else code style for reduced code for rounding floatsIs floating point math broken?How do I parse a...
Is '大勢の人' redundant?
Can the Supreme Court overturn an impeachment?
Engineer refusing to file/disclose patents
Why do we read the Megillah by night and by day?
Why did the EU agree to delay the Brexit deadline?
Create all possible words using a set or letters
Journal losing indexing services
Why does the Sun have different day lengths, but not the gas giants?
Biological Blimps: Propulsion
Does a 'pending' US visa application constitute a denial?
Is XSS in canonical link possible?
Proving a function is onto where f(x)=|x|.
Is Asuka Langley-Soryu disgusted by Shinji?
Delete database accidentally by a bash, rescue please
Longest common substring in linear time
Is a model fitted to data or is data fitted to a model?
How is flyblackbird.com operating under Part 91K?
GraphicsGrid with a Label for each Column and Row
How do I nest cases?
Diode in opposite direction?
Character escape sequences for ">"
How to set Output path correctly for a Single Image render?
Folder comparison
How much character growth crosses the line into breaking the character
Python if-else code style for reduced code for rounding floats
Is floating point math broken?How do I parse a string to a float or int in Python?Printing increments of 0.1 in c#How can I force division to be floating point? Division keeps rounding down to 0?Double increments in JavaPut integers from stdin in listSelecting lines of a file specified by a list of stringsPerfect ROC curve but imperfect prediction accuracyFor loop break doesn't work PythonHow to classify observations based on their covariates in dataframe and numpy?Parse dataframe with specific column and write to sheets in one excel file
Is there any shorter, more legible code style to solve this problem?
I am trying to classify some float values into interregional folders.
def classify(value):
if value < -0.85 and value >= -0.95:
ts_folder = r'-0.9'
elif value < -0.75 and value >= -0.85:
ts_folder = r'-0.8'
elif value < -0.65 and value >= -0.75:
ts_folder = r'-0.7'
elif value < -0.55 and value >= -0.65:
ts_folder = r'-0.6'
elif value < -0.45 and value >= -0.55:
ts_folder = r'-0.5'
elif value < -0.35 and value >= -0.45:
ts_folder = r'-0.4'
elif value < -0.25 and value >= -0.35:
ts_folder = r'-0.3'
elif value < -0.15 and value >= -0.25:
ts_folder = r'-0.2'
elif value < -0.05 and value >= -0.15:
ts_folder = r'-0.1'
elif value < 0.05 and value >= -0.05:
ts_folder = r'.0'
elif value < 0.15 and value >= 0.05:
ts_folder = r'.1'
elif value < 0.25 and value >= 0.15:
ts_folder = r'.2'
elif value < 0.35 and value >= 0.25:
ts_folder = r'.3'
elif value < 0.45 and value >= 0.35:
ts_folder = r'.4'
elif value < 0.55 and value >= 0.45:
ts_folder = r'.5'
elif value < 0.65 and value >= 0.55:
ts_folder = r'.6'
elif value < 0.75 and value >= 0.65:
ts_folder = r'.7'
elif value < 0.85 and value >= 0.75:
ts_folder = r'.8'
elif value < 0.95 and value >= 0.85:
ts_folder = r'.9'
return ts_folder
python floating-point rounding number-formatting
|
show 1 more comment
Is there any shorter, more legible code style to solve this problem?
I am trying to classify some float values into interregional folders.
def classify(value):
if value < -0.85 and value >= -0.95:
ts_folder = r'-0.9'
elif value < -0.75 and value >= -0.85:
ts_folder = r'-0.8'
elif value < -0.65 and value >= -0.75:
ts_folder = r'-0.7'
elif value < -0.55 and value >= -0.65:
ts_folder = r'-0.6'
elif value < -0.45 and value >= -0.55:
ts_folder = r'-0.5'
elif value < -0.35 and value >= -0.45:
ts_folder = r'-0.4'
elif value < -0.25 and value >= -0.35:
ts_folder = r'-0.3'
elif value < -0.15 and value >= -0.25:
ts_folder = r'-0.2'
elif value < -0.05 and value >= -0.15:
ts_folder = r'-0.1'
elif value < 0.05 and value >= -0.05:
ts_folder = r'.0'
elif value < 0.15 and value >= 0.05:
ts_folder = r'.1'
elif value < 0.25 and value >= 0.15:
ts_folder = r'.2'
elif value < 0.35 and value >= 0.25:
ts_folder = r'.3'
elif value < 0.45 and value >= 0.35:
ts_folder = r'.4'
elif value < 0.55 and value >= 0.45:
ts_folder = r'.5'
elif value < 0.65 and value >= 0.55:
ts_folder = r'.6'
elif value < 0.75 and value >= 0.65:
ts_folder = r'.7'
elif value < 0.85 and value >= 0.75:
ts_folder = r'.8'
elif value < 0.95 and value >= 0.85:
ts_folder = r'.9'
return ts_folder
python floating-point rounding number-formatting
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
8
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
Mar 15 at 12:04
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
7
It's a great way to hide bugs!ts_folder
is undefined for values larger than0.95
or smaller than-0.85
. Also, the result for-0.45
and-0.35
differ by0.2
.
– Eric Duminil
Mar 15 at 17:34
|
show 1 more comment
Is there any shorter, more legible code style to solve this problem?
I am trying to classify some float values into interregional folders.
def classify(value):
if value < -0.85 and value >= -0.95:
ts_folder = r'-0.9'
elif value < -0.75 and value >= -0.85:
ts_folder = r'-0.8'
elif value < -0.65 and value >= -0.75:
ts_folder = r'-0.7'
elif value < -0.55 and value >= -0.65:
ts_folder = r'-0.6'
elif value < -0.45 and value >= -0.55:
ts_folder = r'-0.5'
elif value < -0.35 and value >= -0.45:
ts_folder = r'-0.4'
elif value < -0.25 and value >= -0.35:
ts_folder = r'-0.3'
elif value < -0.15 and value >= -0.25:
ts_folder = r'-0.2'
elif value < -0.05 and value >= -0.15:
ts_folder = r'-0.1'
elif value < 0.05 and value >= -0.05:
ts_folder = r'.0'
elif value < 0.15 and value >= 0.05:
ts_folder = r'.1'
elif value < 0.25 and value >= 0.15:
ts_folder = r'.2'
elif value < 0.35 and value >= 0.25:
ts_folder = r'.3'
elif value < 0.45 and value >= 0.35:
ts_folder = r'.4'
elif value < 0.55 and value >= 0.45:
ts_folder = r'.5'
elif value < 0.65 and value >= 0.55:
ts_folder = r'.6'
elif value < 0.75 and value >= 0.65:
ts_folder = r'.7'
elif value < 0.85 and value >= 0.75:
ts_folder = r'.8'
elif value < 0.95 and value >= 0.85:
ts_folder = r'.9'
return ts_folder
python floating-point rounding number-formatting
Is there any shorter, more legible code style to solve this problem?
I am trying to classify some float values into interregional folders.
def classify(value):
if value < -0.85 and value >= -0.95:
ts_folder = r'-0.9'
elif value < -0.75 and value >= -0.85:
ts_folder = r'-0.8'
elif value < -0.65 and value >= -0.75:
ts_folder = r'-0.7'
elif value < -0.55 and value >= -0.65:
ts_folder = r'-0.6'
elif value < -0.45 and value >= -0.55:
ts_folder = r'-0.5'
elif value < -0.35 and value >= -0.45:
ts_folder = r'-0.4'
elif value < -0.25 and value >= -0.35:
ts_folder = r'-0.3'
elif value < -0.15 and value >= -0.25:
ts_folder = r'-0.2'
elif value < -0.05 and value >= -0.15:
ts_folder = r'-0.1'
elif value < 0.05 and value >= -0.05:
ts_folder = r'.0'
elif value < 0.15 and value >= 0.05:
ts_folder = r'.1'
elif value < 0.25 and value >= 0.15:
ts_folder = r'.2'
elif value < 0.35 and value >= 0.25:
ts_folder = r'.3'
elif value < 0.45 and value >= 0.35:
ts_folder = r'.4'
elif value < 0.55 and value >= 0.45:
ts_folder = r'.5'
elif value < 0.65 and value >= 0.55:
ts_folder = r'.6'
elif value < 0.75 and value >= 0.65:
ts_folder = r'.7'
elif value < 0.85 and value >= 0.75:
ts_folder = r'.8'
elif value < 0.95 and value >= 0.85:
ts_folder = r'.9'
return ts_folder
python floating-point rounding number-formatting
python floating-point rounding number-formatting
edited Mar 15 at 23:05
smci
15.4k678109
15.4k678109
asked Mar 15 at 10:44
Kuang 鄺世銘Kuang 鄺世銘
11516
11516
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
8
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
Mar 15 at 12:04
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
7
It's a great way to hide bugs!ts_folder
is undefined for values larger than0.95
or smaller than-0.85
. Also, the result for-0.45
and-0.35
differ by0.2
.
– Eric Duminil
Mar 15 at 17:34
|
show 1 more comment
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
8
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
Mar 15 at 12:04
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
7
It's a great way to hide bugs!ts_folder
is undefined for values larger than0.95
or smaller than-0.85
. Also, the result for-0.45
and-0.35
differ by0.2
.
– Eric Duminil
Mar 15 at 17:34
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
8
8
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
Mar 15 at 12:04
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
Mar 15 at 12:04
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
7
7
It's a great way to hide bugs!
ts_folder
is undefined for values larger than 0.95
or smaller than -0.85
. Also, the result for -0.45
and -0.35
differ by 0.2
.– Eric Duminil
Mar 15 at 17:34
It's a great way to hide bugs!
ts_folder
is undefined for values larger than 0.95
or smaller than -0.85
. Also, the result for -0.45
and -0.35
differ by 0.2
.– Eric Duminil
Mar 15 at 17:34
|
show 1 more comment
12 Answers
12
active
oldest
votes
Specific solution
There is no real general solution, but in your case you can use the following expression.
ts_folder = r'{:.1f}'.format(round(value, 1))
General solution
If you actually need some kind of generalization, notice that any non-linear pattern will cause trouble. Although, there is a way to shorten the code.
def classify(key, intervals):
for lo, hi, value in intervals:
if lo <= key < hi:
return value
else:
... # return a default value or None
# A list of tuples (lo, hi, key) which associates any value in the lo to hi interval to key
intervals = [
(value / 10 - 0.05, value / 10 + 0.05, r'{:.1f}'.format(value / 10))
for value in range(-9, 10)
]
value = -0.73
ts_folder = classify(value, intervals) # r'-0.7'
Notice that the above is still not totally safe from some float rounding error. You can add precision by manually typing down the intervals
list instead of using a comprehension.
Continuous intervals
If the intervals in your data are continuous, that is there is no gap between them, as in your example, then we can use some optimizations. Namely, we can store only the higher bound of each interval in the list. Then by keeping those sorted, we can use bisect
for efficient lookup.
import bisect
def value_from_hi(hi):
return r'{:.1f}'.format(hi - 0.05)
def classify(key, boundaries):
i = bisect.bisect_right(boundaries, key)
if i < len(boundaries):
return value_from_hi(boundaries[i])
else:
... # return some default value
# Sorted upper bounds
boundaries = [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05,
0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = classify(-0.32, boundaries) # r'-0.3'
Important note: the choice of using the higher bounds and bisect_right
is due to the fact the higher bounds are excluded in your example. If the lower bounds were excluded, then we would have to use those with bisect_left
.
Also note that you may want to treat numbers out of the range [-0.95, 0.95[ in some special way and note just leave those to bisect
.
2
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
Mar 15 at 15:26
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomesfor hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
Floats comparison are tricky. On my computer, your specific solution returns different values for[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.
– Eric Duminil
Mar 15 at 17:32
3
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
Mar 15 at 19:54
add a comment |
The bisect module will do exactly the right lookup for finding the right bin from a list of breakpoints. In fact, the example in the documentation is exactly a case like this:
The bisect() function is generally useful for categorizing numeric data. This example uses bisect() to look up a letter grade for an exam total (say) based on a set of ordered numeric breakpoints: 85 and up is an ‘A’, 75..84 is a ‘B’, etc.
>>> grades = "FEDCBA"
>>> breakpoints = [30, 44, 66, 75, 85]
>>> from bisect import bisect
>>> def grade(total):
... return grades[bisect(breakpoints, total)]
>>> grade(66)
'C'
>>> map(grade, [33, 99, 77, 44, 12, 88])
['E', 'A', 'B', 'D', 'F', 'A']
Instead of a string for the value lookups, you'd want a list of strings for the exact folder names you need for each range of values. For example:
breakpoints = [-0.85, -0.75, -0.65]
folders = [r'-0.9', r'-0.8', r'-0.7']
foldername = folders[bisect(breakpoints, -0.72)]
If you can automate even part of this table generation (using round()
, or something similar), of course you should.
add a comment |
One of the first rules with a block of code like this, is to always make the comparisons be in the same direction. So instead of
elif value < -0.75 and value >= -0.85:
write
elif -0.85 <= value and value < -0.75:
At this point you can observe that python allows chaining of comparisons, so you can write:
elif -0.85 <= value < -0.75:
Which is an improvement itself. Alternatively, you can observe this is an ordered list of comparisons, so if you add in an initial comparisons, you can just write
if value < -0.95: ts_folder = ''
elif value < -0.85: ts_folder = r'-0.9'
elif value < -0.75: ts_folder = r'-0.8'
elif value < -0.65: ts_folder = r'-0.7'
elif value < -0.55: ts_folder = r'-0.6'
elif value < -0.45: ts_folder = r'-0.5'
elif value < -0.35: ts_folder = r'-0.4'
elif value < -0.25: ts_folder = r'-0.3'
elif value < -0.15: ts_folder = r'-0.2'
elif value < -0.05: ts_folder = r'-0.1'
elif value < 0.05: ts_folder = r'.0'
elif value < 0.15: ts_folder = r'.1'
elif value < 0.25: ts_folder = r'.2'
elif value < 0.35: ts_folder = r'.3'
elif value < 0.45: ts_folder = r'.4'
elif value < 0.55: ts_folder = r'.5'
elif value < 0.65: ts_folder = r'.6'
elif value < 0.75: ts_folder = r'.7'
elif value < 0.85: ts_folder = r'.8'
elif value < 0.95: ts_folder = r'.9'
else: ts_folder = ''
That's still quite long, but a) it's a lot more readable; b) it has explicit code to handle value < -0.95 or 0.95 <= value
add a comment |
All answers revolve around rounding, which seems to be fine in this case, but just for the sake of argument I'd like to also point out a cool python use of dictionaries which is often described as an alternative to other languages switch(es) and that in turn allow for arbitrary values.
ranges = {
(-0.85, -0.95): r'-0.9',
(-0.75, -0.85): r'-0.8',
(-0.65, -0.75): r'-0.7',
(-0.55, -0.65): r'-0.6'
...
}
def classify (value):
for (ceiling, floor), rounded_value in ranges.items():
if floor <= value < ceiling:
return rounded_value
Output:
>>> classify(-0.78)
-0.8
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless__getitem__
access).
– bruno desthuilliers
Mar 15 at 12:39
2
@chepner By editing this code you have made it not work; it indexes intoranges
withcurrent_value
which is not defined (because you deleted it).
– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
|
show 4 more comments
You can use the round()
built-in :
ts_folder = "\" + str(round(value + 1e-16, 1)) # To round values like .05 to .1, not .0
if ts_folder == r"-0.0": ts_folder = r".0"
More on round()
1
It doesn't seem to work for[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.
– Eric Duminil
Mar 15 at 17:41
Still have an issue with[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.
– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to0
(because-0.0 == 0.0
) and if so set it to 0.
– Wlerin
Mar 17 at 7:46
add a comment |
Actually in Python 3 .85
will be round to .8
. As per the question .85
should be round to .9
.
Can you try the following:
round2 = lambda x, y=None: round(x+1e-15, y)
ts_folder = r'{}'.format(str(round2(value, 1)))
Output:
>>> round2(.85, 1)
0.9
>>> round2(-.85, 1)
-0.8
As Wlerin said under my post :-.05
,-.04
,-.03
etc will alas be transformed to-0.0
, and not.0
– Fukiyel
Mar 17 at 7:00
add a comment |
How about turning it into a loop?
def classify(value):
i = -5
while i < 95:
if value < (i + 10) / 100.0 and value >= i / 100.0:
return '\' + repr((i + 5) / 100.0)
i += 10
it's not efficient by any means, but it's equivalent to what you have, just shorter.
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
add a comment |
You don't need the and value >= -.85
in elif value < -0.75 and value >= -0.85:
; if the value isn't greater than or equal to -.85, then you won't reach the elif. You can also just turn all the elif
s into if
by having each one return immediately.
In this case, since you have the boundaries at regular intervals, you can just round (in the general case of regular intervals, you may have to divide and then round, for instance if the intervals are at every three units, then you would divide the number by three and round). In the general case, it's faster to store the boundaries in a tree structure, and then do a binary search for where the item goes.
Doing a binary search explicitly would be something like this:
def classify(value):
if value < -.05:
if value < -.45:
if value < -.65:
if value < -.85:
if value < -.95:
return None
return r'-0.9'
if value < -.75:
return r'-0.8'
return r'-0.7'
...
Although this code is harder to read than yours, it runs in time logarithmic rather than linear with respect to the number of boundaries.
If the number of items is significantly larger than the number of boundaries, it would probably be faster to actually create a tree of the items, and insert the boundaries.
You could also create a list, sort it, and then look at the index. For instance, compare (sorted([(_-9.5)/10 for _ in range(20)]+[x]).index(x)-9)/10
to your function.
add a comment |
Take a look at the round()
function in python. Maybe you can work it out without the if.
With this function you can specify the number of digits you need to keep.
For example :
x = round(5.76543, 2)
print(x)
That code will print 5.77
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
add a comment |
Many of these answers suggest some kind of rounding as a solution. Unfortunately, there are three problems with using rounding for this purpose, and at the time of writing all of them fell prey to at least one.
- Floating point representation of decimal values is inexact. For example, the float
0.85
is in fact0.8499999999999999777955395...
. - round() uses ties-round-to-even, also known as scientific or banker's rounding, rather than the arithmetic rounding many of us learned in school. This means e.g. 0.85 rounds to 0.8 instead of 0.9, and 0.25 rounds to 0.2 instead of 0.3.
- very small negative floats (and Decimals) round up to
-0.0
rather than0.0
as the OP's mapping requires.
These can all be solved using the Decimal module, though not as prettily as I'd like:
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
def classify(value):
number = Decimal('{:.2f}'.format(value))
if number < 0:
round_method = ROUND_HALF_DOWN
else:
round_method = ROUND_HALF_UP
rounded_number = number.quantize(Decimal('0.1'), rounding=round_method)
if rounded_number == 0.0:
rounded_number = Decimal('0.0')
return r'{}'.format(rounded_number)
Both ROUND_HALF_DOWN and ROUND_HALF_UP are required as ROUND_HALF_UP actually rounds away from zero rather than towards Infinity. .quantize
rounds a Decimal value to the places given by the first argument, and allows us to specify a rounding method.
Bonus: Bisect Breakpoints using range()
For the bisect solutions, this will generate the breakpoints used by the OP:
from decimal import Decimal
breakpoints = [Decimal('{}e-2'.format(e)) for e in range(-85, 96, 10)]
add a comment |
Try something like this, if you don't like loops:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = [ r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
idx = [value >= end for end in endpts].index(False)
if not idx:
raise ValueError('Value outside of range')
return ts_folder[idx-1]
Of course, the loop is just "hidden" in the list comprehension.
Obviously, in this example, it would be better to generate endpts
and ts_fol
programmatically rather than writing them all out, but you indicated that in the real situation the endpoints and values aren't so straightforward.
This raises a ValueError
if value
≥ 0.95 (because False
is not found in the list comprehension) or if value
< -0.95 (because then idx
is 0); the original version raises a UnboundLocalError
in these cases.
You could also save three lines and skip a few comparisons by doing this:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_fol = [ None, r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
return next((ts for ts, end in zip(ts_fol, endpts) if value < end), None)
This version returns None
rather than raising exceptions for any value outside the bounds.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55180829%2fpython-if-else-code-style-for-reduced-code-for-rounding-floats%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
12 Answers
12
active
oldest
votes
12 Answers
12
active
oldest
votes
active
oldest
votes
active
oldest
votes
Specific solution
There is no real general solution, but in your case you can use the following expression.
ts_folder = r'{:.1f}'.format(round(value, 1))
General solution
If you actually need some kind of generalization, notice that any non-linear pattern will cause trouble. Although, there is a way to shorten the code.
def classify(key, intervals):
for lo, hi, value in intervals:
if lo <= key < hi:
return value
else:
... # return a default value or None
# A list of tuples (lo, hi, key) which associates any value in the lo to hi interval to key
intervals = [
(value / 10 - 0.05, value / 10 + 0.05, r'{:.1f}'.format(value / 10))
for value in range(-9, 10)
]
value = -0.73
ts_folder = classify(value, intervals) # r'-0.7'
Notice that the above is still not totally safe from some float rounding error. You can add precision by manually typing down the intervals
list instead of using a comprehension.
Continuous intervals
If the intervals in your data are continuous, that is there is no gap between them, as in your example, then we can use some optimizations. Namely, we can store only the higher bound of each interval in the list. Then by keeping those sorted, we can use bisect
for efficient lookup.
import bisect
def value_from_hi(hi):
return r'{:.1f}'.format(hi - 0.05)
def classify(key, boundaries):
i = bisect.bisect_right(boundaries, key)
if i < len(boundaries):
return value_from_hi(boundaries[i])
else:
... # return some default value
# Sorted upper bounds
boundaries = [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05,
0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = classify(-0.32, boundaries) # r'-0.3'
Important note: the choice of using the higher bounds and bisect_right
is due to the fact the higher bounds are excluded in your example. If the lower bounds were excluded, then we would have to use those with bisect_left
.
Also note that you may want to treat numbers out of the range [-0.95, 0.95[ in some special way and note just leave those to bisect
.
2
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
Mar 15 at 15:26
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomesfor hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
Floats comparison are tricky. On my computer, your specific solution returns different values for[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.
– Eric Duminil
Mar 15 at 17:32
3
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
Mar 15 at 19:54
add a comment |
Specific solution
There is no real general solution, but in your case you can use the following expression.
ts_folder = r'{:.1f}'.format(round(value, 1))
General solution
If you actually need some kind of generalization, notice that any non-linear pattern will cause trouble. Although, there is a way to shorten the code.
def classify(key, intervals):
for lo, hi, value in intervals:
if lo <= key < hi:
return value
else:
... # return a default value or None
# A list of tuples (lo, hi, key) which associates any value in the lo to hi interval to key
intervals = [
(value / 10 - 0.05, value / 10 + 0.05, r'{:.1f}'.format(value / 10))
for value in range(-9, 10)
]
value = -0.73
ts_folder = classify(value, intervals) # r'-0.7'
Notice that the above is still not totally safe from some float rounding error. You can add precision by manually typing down the intervals
list instead of using a comprehension.
Continuous intervals
If the intervals in your data are continuous, that is there is no gap between them, as in your example, then we can use some optimizations. Namely, we can store only the higher bound of each interval in the list. Then by keeping those sorted, we can use bisect
for efficient lookup.
import bisect
def value_from_hi(hi):
return r'{:.1f}'.format(hi - 0.05)
def classify(key, boundaries):
i = bisect.bisect_right(boundaries, key)
if i < len(boundaries):
return value_from_hi(boundaries[i])
else:
... # return some default value
# Sorted upper bounds
boundaries = [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05,
0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = classify(-0.32, boundaries) # r'-0.3'
Important note: the choice of using the higher bounds and bisect_right
is due to the fact the higher bounds are excluded in your example. If the lower bounds were excluded, then we would have to use those with bisect_left
.
Also note that you may want to treat numbers out of the range [-0.95, 0.95[ in some special way and note just leave those to bisect
.
2
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
Mar 15 at 15:26
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomesfor hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
Floats comparison are tricky. On my computer, your specific solution returns different values for[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.
– Eric Duminil
Mar 15 at 17:32
3
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
Mar 15 at 19:54
add a comment |
Specific solution
There is no real general solution, but in your case you can use the following expression.
ts_folder = r'{:.1f}'.format(round(value, 1))
General solution
If you actually need some kind of generalization, notice that any non-linear pattern will cause trouble. Although, there is a way to shorten the code.
def classify(key, intervals):
for lo, hi, value in intervals:
if lo <= key < hi:
return value
else:
... # return a default value or None
# A list of tuples (lo, hi, key) which associates any value in the lo to hi interval to key
intervals = [
(value / 10 - 0.05, value / 10 + 0.05, r'{:.1f}'.format(value / 10))
for value in range(-9, 10)
]
value = -0.73
ts_folder = classify(value, intervals) # r'-0.7'
Notice that the above is still not totally safe from some float rounding error. You can add precision by manually typing down the intervals
list instead of using a comprehension.
Continuous intervals
If the intervals in your data are continuous, that is there is no gap between them, as in your example, then we can use some optimizations. Namely, we can store only the higher bound of each interval in the list. Then by keeping those sorted, we can use bisect
for efficient lookup.
import bisect
def value_from_hi(hi):
return r'{:.1f}'.format(hi - 0.05)
def classify(key, boundaries):
i = bisect.bisect_right(boundaries, key)
if i < len(boundaries):
return value_from_hi(boundaries[i])
else:
... # return some default value
# Sorted upper bounds
boundaries = [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05,
0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = classify(-0.32, boundaries) # r'-0.3'
Important note: the choice of using the higher bounds and bisect_right
is due to the fact the higher bounds are excluded in your example. If the lower bounds were excluded, then we would have to use those with bisect_left
.
Also note that you may want to treat numbers out of the range [-0.95, 0.95[ in some special way and note just leave those to bisect
.
Specific solution
There is no real general solution, but in your case you can use the following expression.
ts_folder = r'{:.1f}'.format(round(value, 1))
General solution
If you actually need some kind of generalization, notice that any non-linear pattern will cause trouble. Although, there is a way to shorten the code.
def classify(key, intervals):
for lo, hi, value in intervals:
if lo <= key < hi:
return value
else:
... # return a default value or None
# A list of tuples (lo, hi, key) which associates any value in the lo to hi interval to key
intervals = [
(value / 10 - 0.05, value / 10 + 0.05, r'{:.1f}'.format(value / 10))
for value in range(-9, 10)
]
value = -0.73
ts_folder = classify(value, intervals) # r'-0.7'
Notice that the above is still not totally safe from some float rounding error. You can add precision by manually typing down the intervals
list instead of using a comprehension.
Continuous intervals
If the intervals in your data are continuous, that is there is no gap between them, as in your example, then we can use some optimizations. Namely, we can store only the higher bound of each interval in the list. Then by keeping those sorted, we can use bisect
for efficient lookup.
import bisect
def value_from_hi(hi):
return r'{:.1f}'.format(hi - 0.05)
def classify(key, boundaries):
i = bisect.bisect_right(boundaries, key)
if i < len(boundaries):
return value_from_hi(boundaries[i])
else:
... # return some default value
# Sorted upper bounds
boundaries = [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05,
0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = classify(-0.32, boundaries) # r'-0.3'
Important note: the choice of using the higher bounds and bisect_right
is due to the fact the higher bounds are excluded in your example. If the lower bounds were excluded, then we would have to use those with bisect_left
.
Also note that you may want to treat numbers out of the range [-0.95, 0.95[ in some special way and note just leave those to bisect
.
edited Mar 17 at 0:21
answered Mar 15 at 10:58
Olivier MelançonOlivier Melançon
14.1k22142
14.1k22142
2
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
Mar 15 at 15:26
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomesfor hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
Floats comparison are tricky. On my computer, your specific solution returns different values for[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.
– Eric Duminil
Mar 15 at 17:32
3
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
Mar 15 at 19:54
add a comment |
2
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
Mar 15 at 15:26
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomesfor hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
Floats comparison are tricky. On my computer, your specific solution returns different values for[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.
– Eric Duminil
Mar 15 at 17:32
3
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
Mar 15 at 19:54
2
2
Note that the OP had
if lo <=key < hi
.– Martin Bonner
Mar 15 at 15:26
Note that the OP had
if lo <=key < hi
.– Martin Bonner
Mar 15 at 15:26
1
1
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomes
for hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
If the intervals are supposed to be contiguous, you can require that they are supplied sorted (low to high), and then just have the intervals be (hi, value), and then the loop becomes
for hi, value in intervals: if key < hi: return value
– Martin Bonner
Mar 15 at 15:27
1
1
Floats comparison are tricky. On my computer, your specific solution returns different values for
[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.– Eric Duminil
Mar 15 at 17:32
Floats comparison are tricky. On my computer, your specific solution returns different values for
[-0.75, -0.65, -0.55, -0.45, -0.05, -0.04, -0.03, -0.02, -0.01, 0.15, 0.25, 0.35, 0.85]
compared to OP's code.– Eric Duminil
Mar 15 at 17:32
3
3
Assuming the intervals partition a range, binary search with the
bisect
module would be a good option.– user2357112
Mar 15 at 19:54
Assuming the intervals partition a range, binary search with the
bisect
module would be a good option.– user2357112
Mar 15 at 19:54
add a comment |
The bisect module will do exactly the right lookup for finding the right bin from a list of breakpoints. In fact, the example in the documentation is exactly a case like this:
The bisect() function is generally useful for categorizing numeric data. This example uses bisect() to look up a letter grade for an exam total (say) based on a set of ordered numeric breakpoints: 85 and up is an ‘A’, 75..84 is a ‘B’, etc.
>>> grades = "FEDCBA"
>>> breakpoints = [30, 44, 66, 75, 85]
>>> from bisect import bisect
>>> def grade(total):
... return grades[bisect(breakpoints, total)]
>>> grade(66)
'C'
>>> map(grade, [33, 99, 77, 44, 12, 88])
['E', 'A', 'B', 'D', 'F', 'A']
Instead of a string for the value lookups, you'd want a list of strings for the exact folder names you need for each range of values. For example:
breakpoints = [-0.85, -0.75, -0.65]
folders = [r'-0.9', r'-0.8', r'-0.7']
foldername = folders[bisect(breakpoints, -0.72)]
If you can automate even part of this table generation (using round()
, or something similar), of course you should.
add a comment |
The bisect module will do exactly the right lookup for finding the right bin from a list of breakpoints. In fact, the example in the documentation is exactly a case like this:
The bisect() function is generally useful for categorizing numeric data. This example uses bisect() to look up a letter grade for an exam total (say) based on a set of ordered numeric breakpoints: 85 and up is an ‘A’, 75..84 is a ‘B’, etc.
>>> grades = "FEDCBA"
>>> breakpoints = [30, 44, 66, 75, 85]
>>> from bisect import bisect
>>> def grade(total):
... return grades[bisect(breakpoints, total)]
>>> grade(66)
'C'
>>> map(grade, [33, 99, 77, 44, 12, 88])
['E', 'A', 'B', 'D', 'F', 'A']
Instead of a string for the value lookups, you'd want a list of strings for the exact folder names you need for each range of values. For example:
breakpoints = [-0.85, -0.75, -0.65]
folders = [r'-0.9', r'-0.8', r'-0.7']
foldername = folders[bisect(breakpoints, -0.72)]
If you can automate even part of this table generation (using round()
, or something similar), of course you should.
add a comment |
The bisect module will do exactly the right lookup for finding the right bin from a list of breakpoints. In fact, the example in the documentation is exactly a case like this:
The bisect() function is generally useful for categorizing numeric data. This example uses bisect() to look up a letter grade for an exam total (say) based on a set of ordered numeric breakpoints: 85 and up is an ‘A’, 75..84 is a ‘B’, etc.
>>> grades = "FEDCBA"
>>> breakpoints = [30, 44, 66, 75, 85]
>>> from bisect import bisect
>>> def grade(total):
... return grades[bisect(breakpoints, total)]
>>> grade(66)
'C'
>>> map(grade, [33, 99, 77, 44, 12, 88])
['E', 'A', 'B', 'D', 'F', 'A']
Instead of a string for the value lookups, you'd want a list of strings for the exact folder names you need for each range of values. For example:
breakpoints = [-0.85, -0.75, -0.65]
folders = [r'-0.9', r'-0.8', r'-0.7']
foldername = folders[bisect(breakpoints, -0.72)]
If you can automate even part of this table generation (using round()
, or something similar), of course you should.
The bisect module will do exactly the right lookup for finding the right bin from a list of breakpoints. In fact, the example in the documentation is exactly a case like this:
The bisect() function is generally useful for categorizing numeric data. This example uses bisect() to look up a letter grade for an exam total (say) based on a set of ordered numeric breakpoints: 85 and up is an ‘A’, 75..84 is a ‘B’, etc.
>>> grades = "FEDCBA"
>>> breakpoints = [30, 44, 66, 75, 85]
>>> from bisect import bisect
>>> def grade(total):
... return grades[bisect(breakpoints, total)]
>>> grade(66)
'C'
>>> map(grade, [33, 99, 77, 44, 12, 88])
['E', 'A', 'B', 'D', 'F', 'A']
Instead of a string for the value lookups, you'd want a list of strings for the exact folder names you need for each range of values. For example:
breakpoints = [-0.85, -0.75, -0.65]
folders = [r'-0.9', r'-0.8', r'-0.7']
foldername = folders[bisect(breakpoints, -0.72)]
If you can automate even part of this table generation (using round()
, or something similar), of course you should.
answered Mar 15 at 17:14
PeterPeter
11.7k1847
11.7k1847
add a comment |
add a comment |
One of the first rules with a block of code like this, is to always make the comparisons be in the same direction. So instead of
elif value < -0.75 and value >= -0.85:
write
elif -0.85 <= value and value < -0.75:
At this point you can observe that python allows chaining of comparisons, so you can write:
elif -0.85 <= value < -0.75:
Which is an improvement itself. Alternatively, you can observe this is an ordered list of comparisons, so if you add in an initial comparisons, you can just write
if value < -0.95: ts_folder = ''
elif value < -0.85: ts_folder = r'-0.9'
elif value < -0.75: ts_folder = r'-0.8'
elif value < -0.65: ts_folder = r'-0.7'
elif value < -0.55: ts_folder = r'-0.6'
elif value < -0.45: ts_folder = r'-0.5'
elif value < -0.35: ts_folder = r'-0.4'
elif value < -0.25: ts_folder = r'-0.3'
elif value < -0.15: ts_folder = r'-0.2'
elif value < -0.05: ts_folder = r'-0.1'
elif value < 0.05: ts_folder = r'.0'
elif value < 0.15: ts_folder = r'.1'
elif value < 0.25: ts_folder = r'.2'
elif value < 0.35: ts_folder = r'.3'
elif value < 0.45: ts_folder = r'.4'
elif value < 0.55: ts_folder = r'.5'
elif value < 0.65: ts_folder = r'.6'
elif value < 0.75: ts_folder = r'.7'
elif value < 0.85: ts_folder = r'.8'
elif value < 0.95: ts_folder = r'.9'
else: ts_folder = ''
That's still quite long, but a) it's a lot more readable; b) it has explicit code to handle value < -0.95 or 0.95 <= value
add a comment |
One of the first rules with a block of code like this, is to always make the comparisons be in the same direction. So instead of
elif value < -0.75 and value >= -0.85:
write
elif -0.85 <= value and value < -0.75:
At this point you can observe that python allows chaining of comparisons, so you can write:
elif -0.85 <= value < -0.75:
Which is an improvement itself. Alternatively, you can observe this is an ordered list of comparisons, so if you add in an initial comparisons, you can just write
if value < -0.95: ts_folder = ''
elif value < -0.85: ts_folder = r'-0.9'
elif value < -0.75: ts_folder = r'-0.8'
elif value < -0.65: ts_folder = r'-0.7'
elif value < -0.55: ts_folder = r'-0.6'
elif value < -0.45: ts_folder = r'-0.5'
elif value < -0.35: ts_folder = r'-0.4'
elif value < -0.25: ts_folder = r'-0.3'
elif value < -0.15: ts_folder = r'-0.2'
elif value < -0.05: ts_folder = r'-0.1'
elif value < 0.05: ts_folder = r'.0'
elif value < 0.15: ts_folder = r'.1'
elif value < 0.25: ts_folder = r'.2'
elif value < 0.35: ts_folder = r'.3'
elif value < 0.45: ts_folder = r'.4'
elif value < 0.55: ts_folder = r'.5'
elif value < 0.65: ts_folder = r'.6'
elif value < 0.75: ts_folder = r'.7'
elif value < 0.85: ts_folder = r'.8'
elif value < 0.95: ts_folder = r'.9'
else: ts_folder = ''
That's still quite long, but a) it's a lot more readable; b) it has explicit code to handle value < -0.95 or 0.95 <= value
add a comment |
One of the first rules with a block of code like this, is to always make the comparisons be in the same direction. So instead of
elif value < -0.75 and value >= -0.85:
write
elif -0.85 <= value and value < -0.75:
At this point you can observe that python allows chaining of comparisons, so you can write:
elif -0.85 <= value < -0.75:
Which is an improvement itself. Alternatively, you can observe this is an ordered list of comparisons, so if you add in an initial comparisons, you can just write
if value < -0.95: ts_folder = ''
elif value < -0.85: ts_folder = r'-0.9'
elif value < -0.75: ts_folder = r'-0.8'
elif value < -0.65: ts_folder = r'-0.7'
elif value < -0.55: ts_folder = r'-0.6'
elif value < -0.45: ts_folder = r'-0.5'
elif value < -0.35: ts_folder = r'-0.4'
elif value < -0.25: ts_folder = r'-0.3'
elif value < -0.15: ts_folder = r'-0.2'
elif value < -0.05: ts_folder = r'-0.1'
elif value < 0.05: ts_folder = r'.0'
elif value < 0.15: ts_folder = r'.1'
elif value < 0.25: ts_folder = r'.2'
elif value < 0.35: ts_folder = r'.3'
elif value < 0.45: ts_folder = r'.4'
elif value < 0.55: ts_folder = r'.5'
elif value < 0.65: ts_folder = r'.6'
elif value < 0.75: ts_folder = r'.7'
elif value < 0.85: ts_folder = r'.8'
elif value < 0.95: ts_folder = r'.9'
else: ts_folder = ''
That's still quite long, but a) it's a lot more readable; b) it has explicit code to handle value < -0.95 or 0.95 <= value
One of the first rules with a block of code like this, is to always make the comparisons be in the same direction. So instead of
elif value < -0.75 and value >= -0.85:
write
elif -0.85 <= value and value < -0.75:
At this point you can observe that python allows chaining of comparisons, so you can write:
elif -0.85 <= value < -0.75:
Which is an improvement itself. Alternatively, you can observe this is an ordered list of comparisons, so if you add in an initial comparisons, you can just write
if value < -0.95: ts_folder = ''
elif value < -0.85: ts_folder = r'-0.9'
elif value < -0.75: ts_folder = r'-0.8'
elif value < -0.65: ts_folder = r'-0.7'
elif value < -0.55: ts_folder = r'-0.6'
elif value < -0.45: ts_folder = r'-0.5'
elif value < -0.35: ts_folder = r'-0.4'
elif value < -0.25: ts_folder = r'-0.3'
elif value < -0.15: ts_folder = r'-0.2'
elif value < -0.05: ts_folder = r'-0.1'
elif value < 0.05: ts_folder = r'.0'
elif value < 0.15: ts_folder = r'.1'
elif value < 0.25: ts_folder = r'.2'
elif value < 0.35: ts_folder = r'.3'
elif value < 0.45: ts_folder = r'.4'
elif value < 0.55: ts_folder = r'.5'
elif value < 0.65: ts_folder = r'.6'
elif value < 0.75: ts_folder = r'.7'
elif value < 0.85: ts_folder = r'.8'
elif value < 0.95: ts_folder = r'.9'
else: ts_folder = ''
That's still quite long, but a) it's a lot more readable; b) it has explicit code to handle value < -0.95 or 0.95 <= value
edited Mar 16 at 22:18
RonJohn
139116
139116
answered Mar 15 at 15:24
Martin BonnerMartin Bonner
23.7k33267
23.7k33267
add a comment |
add a comment |
All answers revolve around rounding, which seems to be fine in this case, but just for the sake of argument I'd like to also point out a cool python use of dictionaries which is often described as an alternative to other languages switch(es) and that in turn allow for arbitrary values.
ranges = {
(-0.85, -0.95): r'-0.9',
(-0.75, -0.85): r'-0.8',
(-0.65, -0.75): r'-0.7',
(-0.55, -0.65): r'-0.6'
...
}
def classify (value):
for (ceiling, floor), rounded_value in ranges.items():
if floor <= value < ceiling:
return rounded_value
Output:
>>> classify(-0.78)
-0.8
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless__getitem__
access).
– bruno desthuilliers
Mar 15 at 12:39
2
@chepner By editing this code you have made it not work; it indexes intoranges
withcurrent_value
which is not defined (because you deleted it).
– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
|
show 4 more comments
All answers revolve around rounding, which seems to be fine in this case, but just for the sake of argument I'd like to also point out a cool python use of dictionaries which is often described as an alternative to other languages switch(es) and that in turn allow for arbitrary values.
ranges = {
(-0.85, -0.95): r'-0.9',
(-0.75, -0.85): r'-0.8',
(-0.65, -0.75): r'-0.7',
(-0.55, -0.65): r'-0.6'
...
}
def classify (value):
for (ceiling, floor), rounded_value in ranges.items():
if floor <= value < ceiling:
return rounded_value
Output:
>>> classify(-0.78)
-0.8
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless__getitem__
access).
– bruno desthuilliers
Mar 15 at 12:39
2
@chepner By editing this code you have made it not work; it indexes intoranges
withcurrent_value
which is not defined (because you deleted it).
– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
|
show 4 more comments
All answers revolve around rounding, which seems to be fine in this case, but just for the sake of argument I'd like to also point out a cool python use of dictionaries which is often described as an alternative to other languages switch(es) and that in turn allow for arbitrary values.
ranges = {
(-0.85, -0.95): r'-0.9',
(-0.75, -0.85): r'-0.8',
(-0.65, -0.75): r'-0.7',
(-0.55, -0.65): r'-0.6'
...
}
def classify (value):
for (ceiling, floor), rounded_value in ranges.items():
if floor <= value < ceiling:
return rounded_value
Output:
>>> classify(-0.78)
-0.8
All answers revolve around rounding, which seems to be fine in this case, but just for the sake of argument I'd like to also point out a cool python use of dictionaries which is often described as an alternative to other languages switch(es) and that in turn allow for arbitrary values.
ranges = {
(-0.85, -0.95): r'-0.9',
(-0.75, -0.85): r'-0.8',
(-0.65, -0.75): r'-0.7',
(-0.55, -0.65): r'-0.6'
...
}
def classify (value):
for (ceiling, floor), rounded_value in ranges.items():
if floor <= value < ceiling:
return rounded_value
Output:
>>> classify(-0.78)
-0.8
edited Mar 15 at 14:38
chepner
258k34249343
258k34249343
answered Mar 15 at 11:47
Hirabayashi TaroHirabayashi Taro
553311
553311
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless__getitem__
access).
– bruno desthuilliers
Mar 15 at 12:39
2
@chepner By editing this code you have made it not work; it indexes intoranges
withcurrent_value
which is not defined (because you deleted it).
– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
|
show 4 more comments
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless__getitem__
access).
– bruno desthuilliers
Mar 15 at 12:39
2
@chepner By editing this code you have made it not work; it indexes intoranges
withcurrent_value
which is not defined (because you deleted it).
– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
20
20
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of
(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless __getitem__
access).– bruno desthuilliers
Mar 15 at 12:39
In this case you're NOT using the "dict dispatch" trick - you're doing a sequential scan, so you'd get the exact same result with a list of
(start, stop, val)
tuples (but with the added overhead of creating a dict and doing a useless __getitem__
access).– bruno desthuilliers
Mar 15 at 12:39
2
2
@chepner By editing this code you have made it not work; it indexes into
ranges
with current_value
which is not defined (because you deleted it).– Arthur Tacca
Mar 15 at 14:36
@chepner By editing this code you have made it not work; it indexes into
ranges
with current_value
which is not defined (because you deleted it).– Arthur Tacca
Mar 15 at 14:36
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers: I would argue the dict is not useless; the code is much more readable, and easy to modify. Sure, it's not efficient (O(n)), but n is small and it may be the right choice in some cases.
– danuker
Mar 17 at 10:19
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@brunodesthuilliers I never said anything about "dict as dispatch table"; I think you confuse me with the author of this answer. I just stumbled across this question and answer and found the answer could not work, and looking into it more found the original answer did work but an editor had broken it while "improving" it. It has since been fixed. Still, if someone considers an answer substantially lacking, I think it makes more sense to post a new one than to totally rewrite the answer in an edit.
– Arthur Tacca
Mar 18 at 12:22
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
@ArthurTacca oops, sorry, there was some confusion indeed (actually with not only the author but also with danuker). I cannot edit my comment anymore so I'll delete and repost an edited version.
– bruno desthuilliers
Mar 18 at 13:09
|
show 4 more comments
You can use the round()
built-in :
ts_folder = "\" + str(round(value + 1e-16, 1)) # To round values like .05 to .1, not .0
if ts_folder == r"-0.0": ts_folder = r".0"
More on round()
1
It doesn't seem to work for[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.
– Eric Duminil
Mar 15 at 17:41
Still have an issue with[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.
– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to0
(because-0.0 == 0.0
) and if so set it to 0.
– Wlerin
Mar 17 at 7:46
add a comment |
You can use the round()
built-in :
ts_folder = "\" + str(round(value + 1e-16, 1)) # To round values like .05 to .1, not .0
if ts_folder == r"-0.0": ts_folder = r".0"
More on round()
1
It doesn't seem to work for[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.
– Eric Duminil
Mar 15 at 17:41
Still have an issue with[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.
– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to0
(because-0.0 == 0.0
) and if so set it to 0.
– Wlerin
Mar 17 at 7:46
add a comment |
You can use the round()
built-in :
ts_folder = "\" + str(round(value + 1e-16, 1)) # To round values like .05 to .1, not .0
if ts_folder == r"-0.0": ts_folder = r".0"
More on round()
You can use the round()
built-in :
ts_folder = "\" + str(round(value + 1e-16, 1)) # To round values like .05 to .1, not .0
if ts_folder == r"-0.0": ts_folder = r".0"
More on round()
edited Mar 17 at 7:53
answered Mar 15 at 10:48
FukiyelFukiyel
1,078317
1,078317
1
It doesn't seem to work for[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.
– Eric Duminil
Mar 15 at 17:41
Still have an issue with[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.
– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to0
(because-0.0 == 0.0
) and if so set it to 0.
– Wlerin
Mar 17 at 7:46
add a comment |
1
It doesn't seem to work for[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.
– Eric Duminil
Mar 15 at 17:41
Still have an issue with[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.
– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to0
(because-0.0 == 0.0
) and if so set it to 0.
– Wlerin
Mar 17 at 7:46
1
1
It doesn't seem to work for
[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.– Eric Duminil
Mar 15 at 17:41
It doesn't seem to work for
[-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, -0.04, -0.03, -0.02, -0.01, 0.0]
, compared to OP's code. I'm not sure if it's a bug or a feature.– Eric Duminil
Mar 15 at 17:41
Still have an issue with
[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.– Wlerin
Mar 17 at 6:25
Still have an issue with
[-0.05, -0.04, -0.03, -0.02, -0.01]
, as they get rounded to -0.0 instead of 0.0 as in the OP.– Wlerin
Mar 17 at 6:25
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
Gosh I forgot that, thanks @Wlerin
– Fukiyel
Mar 17 at 6:55
I suspect you meant to include a
*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to 0
(because -0.0 == 0.0
) and if so set it to 0.– Wlerin
Mar 17 at 7:46
I suspect you meant to include a
*10
inside the round() call as well, but unfortunately this just changes the problem from negative zero to banker's rounding. Perhaps the simplest solution is to round the value using your original method, then check if it's equal to 0
(because -0.0 == 0.0
) and if so set it to 0.– Wlerin
Mar 17 at 7:46
add a comment |
Actually in Python 3 .85
will be round to .8
. As per the question .85
should be round to .9
.
Can you try the following:
round2 = lambda x, y=None: round(x+1e-15, y)
ts_folder = r'{}'.format(str(round2(value, 1)))
Output:
>>> round2(.85, 1)
0.9
>>> round2(-.85, 1)
-0.8
As Wlerin said under my post :-.05
,-.04
,-.03
etc will alas be transformed to-0.0
, and not.0
– Fukiyel
Mar 17 at 7:00
add a comment |
Actually in Python 3 .85
will be round to .8
. As per the question .85
should be round to .9
.
Can you try the following:
round2 = lambda x, y=None: round(x+1e-15, y)
ts_folder = r'{}'.format(str(round2(value, 1)))
Output:
>>> round2(.85, 1)
0.9
>>> round2(-.85, 1)
-0.8
As Wlerin said under my post :-.05
,-.04
,-.03
etc will alas be transformed to-0.0
, and not.0
– Fukiyel
Mar 17 at 7:00
add a comment |
Actually in Python 3 .85
will be round to .8
. As per the question .85
should be round to .9
.
Can you try the following:
round2 = lambda x, y=None: round(x+1e-15, y)
ts_folder = r'{}'.format(str(round2(value, 1)))
Output:
>>> round2(.85, 1)
0.9
>>> round2(-.85, 1)
-0.8
Actually in Python 3 .85
will be round to .8
. As per the question .85
should be round to .9
.
Can you try the following:
round2 = lambda x, y=None: round(x+1e-15, y)
ts_folder = r'{}'.format(str(round2(value, 1)))
Output:
>>> round2(.85, 1)
0.9
>>> round2(-.85, 1)
-0.8
answered Mar 15 at 11:02
JerilJeril
2,4841836
2,4841836
As Wlerin said under my post :-.05
,-.04
,-.03
etc will alas be transformed to-0.0
, and not.0
– Fukiyel
Mar 17 at 7:00
add a comment |
As Wlerin said under my post :-.05
,-.04
,-.03
etc will alas be transformed to-0.0
, and not.0
– Fukiyel
Mar 17 at 7:00
As Wlerin said under my post :
-.05
, -.04
, -.03
etc will alas be transformed to -0.0
, and not .0
– Fukiyel
Mar 17 at 7:00
As Wlerin said under my post :
-.05
, -.04
, -.03
etc will alas be transformed to -0.0
, and not .0
– Fukiyel
Mar 17 at 7:00
add a comment |
How about turning it into a loop?
def classify(value):
i = -5
while i < 95:
if value < (i + 10) / 100.0 and value >= i / 100.0:
return '\' + repr((i + 5) / 100.0)
i += 10
it's not efficient by any means, but it's equivalent to what you have, just shorter.
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
add a comment |
How about turning it into a loop?
def classify(value):
i = -5
while i < 95:
if value < (i + 10) / 100.0 and value >= i / 100.0:
return '\' + repr((i + 5) / 100.0)
i += 10
it's not efficient by any means, but it's equivalent to what you have, just shorter.
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
add a comment |
How about turning it into a loop?
def classify(value):
i = -5
while i < 95:
if value < (i + 10) / 100.0 and value >= i / 100.0:
return '\' + repr((i + 5) / 100.0)
i += 10
it's not efficient by any means, but it's equivalent to what you have, just shorter.
How about turning it into a loop?
def classify(value):
i = -5
while i < 95:
if value < (i + 10) / 100.0 and value >= i / 100.0:
return '\' + repr((i + 5) / 100.0)
i += 10
it's not efficient by any means, but it's equivalent to what you have, just shorter.
answered Mar 15 at 12:33
MehrdadMehrdad
129k90416762
129k90416762
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
add a comment |
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
You need to start at ` i = -95`, though.
– Eric Duminil
Mar 15 at 17:38
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
edited Mar 15 at 18:51
wizzwizz4
3,77511738
3,77511738
answered Mar 15 at 11:02
Asif AkhtarAsif Akhtar
96110
96110
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
add a comment |
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
using Decimal is a good idea, but this definitely doesn't work as is.
– Wlerin
Mar 17 at 6:29
add a comment |
You don't need the and value >= -.85
in elif value < -0.75 and value >= -0.85:
; if the value isn't greater than or equal to -.85, then you won't reach the elif. You can also just turn all the elif
s into if
by having each one return immediately.
In this case, since you have the boundaries at regular intervals, you can just round (in the general case of regular intervals, you may have to divide and then round, for instance if the intervals are at every three units, then you would divide the number by three and round). In the general case, it's faster to store the boundaries in a tree structure, and then do a binary search for where the item goes.
Doing a binary search explicitly would be something like this:
def classify(value):
if value < -.05:
if value < -.45:
if value < -.65:
if value < -.85:
if value < -.95:
return None
return r'-0.9'
if value < -.75:
return r'-0.8'
return r'-0.7'
...
Although this code is harder to read than yours, it runs in time logarithmic rather than linear with respect to the number of boundaries.
If the number of items is significantly larger than the number of boundaries, it would probably be faster to actually create a tree of the items, and insert the boundaries.
You could also create a list, sort it, and then look at the index. For instance, compare (sorted([(_-9.5)/10 for _ in range(20)]+[x]).index(x)-9)/10
to your function.
add a comment |
You don't need the and value >= -.85
in elif value < -0.75 and value >= -0.85:
; if the value isn't greater than or equal to -.85, then you won't reach the elif. You can also just turn all the elif
s into if
by having each one return immediately.
In this case, since you have the boundaries at regular intervals, you can just round (in the general case of regular intervals, you may have to divide and then round, for instance if the intervals are at every three units, then you would divide the number by three and round). In the general case, it's faster to store the boundaries in a tree structure, and then do a binary search for where the item goes.
Doing a binary search explicitly would be something like this:
def classify(value):
if value < -.05:
if value < -.45:
if value < -.65:
if value < -.85:
if value < -.95:
return None
return r'-0.9'
if value < -.75:
return r'-0.8'
return r'-0.7'
...
Although this code is harder to read than yours, it runs in time logarithmic rather than linear with respect to the number of boundaries.
If the number of items is significantly larger than the number of boundaries, it would probably be faster to actually create a tree of the items, and insert the boundaries.
You could also create a list, sort it, and then look at the index. For instance, compare (sorted([(_-9.5)/10 for _ in range(20)]+[x]).index(x)-9)/10
to your function.
add a comment |
You don't need the and value >= -.85
in elif value < -0.75 and value >= -0.85:
; if the value isn't greater than or equal to -.85, then you won't reach the elif. You can also just turn all the elif
s into if
by having each one return immediately.
In this case, since you have the boundaries at regular intervals, you can just round (in the general case of regular intervals, you may have to divide and then round, for instance if the intervals are at every three units, then you would divide the number by three and round). In the general case, it's faster to store the boundaries in a tree structure, and then do a binary search for where the item goes.
Doing a binary search explicitly would be something like this:
def classify(value):
if value < -.05:
if value < -.45:
if value < -.65:
if value < -.85:
if value < -.95:
return None
return r'-0.9'
if value < -.75:
return r'-0.8'
return r'-0.7'
...
Although this code is harder to read than yours, it runs in time logarithmic rather than linear with respect to the number of boundaries.
If the number of items is significantly larger than the number of boundaries, it would probably be faster to actually create a tree of the items, and insert the boundaries.
You could also create a list, sort it, and then look at the index. For instance, compare (sorted([(_-9.5)/10 for _ in range(20)]+[x]).index(x)-9)/10
to your function.
You don't need the and value >= -.85
in elif value < -0.75 and value >= -0.85:
; if the value isn't greater than or equal to -.85, then you won't reach the elif. You can also just turn all the elif
s into if
by having each one return immediately.
In this case, since you have the boundaries at regular intervals, you can just round (in the general case of regular intervals, you may have to divide and then round, for instance if the intervals are at every three units, then you would divide the number by three and round). In the general case, it's faster to store the boundaries in a tree structure, and then do a binary search for where the item goes.
Doing a binary search explicitly would be something like this:
def classify(value):
if value < -.05:
if value < -.45:
if value < -.65:
if value < -.85:
if value < -.95:
return None
return r'-0.9'
if value < -.75:
return r'-0.8'
return r'-0.7'
...
Although this code is harder to read than yours, it runs in time logarithmic rather than linear with respect to the number of boundaries.
If the number of items is significantly larger than the number of boundaries, it would probably be faster to actually create a tree of the items, and insert the boundaries.
You could also create a list, sort it, and then look at the index. For instance, compare (sorted([(_-9.5)/10 for _ in range(20)]+[x]).index(x)-9)/10
to your function.
answered Mar 15 at 16:37
AcccumulationAcccumulation
1,43329
1,43329
add a comment |
add a comment |
Take a look at the round()
function in python. Maybe you can work it out without the if.
With this function you can specify the number of digits you need to keep.
For example :
x = round(5.76543, 2)
print(x)
That code will print 5.77
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
add a comment |
Take a look at the round()
function in python. Maybe you can work it out without the if.
With this function you can specify the number of digits you need to keep.
For example :
x = round(5.76543, 2)
print(x)
That code will print 5.77
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
add a comment |
Take a look at the round()
function in python. Maybe you can work it out without the if.
With this function you can specify the number of digits you need to keep.
For example :
x = round(5.76543, 2)
print(x)
That code will print 5.77
Take a look at the round()
function in python. Maybe you can work it out without the if.
With this function you can specify the number of digits you need to keep.
For example :
x = round(5.76543, 2)
print(x)
That code will print 5.77
edited Mar 15 at 19:34
Olivier Melançon
14.1k22142
14.1k22142
answered Mar 15 at 10:48
MelKoutchMelKoutch
555
555
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
add a comment |
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
2
2
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
Please try and answer with a clear example
– AJS
Mar 15 at 10:52
add a comment |
Many of these answers suggest some kind of rounding as a solution. Unfortunately, there are three problems with using rounding for this purpose, and at the time of writing all of them fell prey to at least one.
- Floating point representation of decimal values is inexact. For example, the float
0.85
is in fact0.8499999999999999777955395...
. - round() uses ties-round-to-even, also known as scientific or banker's rounding, rather than the arithmetic rounding many of us learned in school. This means e.g. 0.85 rounds to 0.8 instead of 0.9, and 0.25 rounds to 0.2 instead of 0.3.
- very small negative floats (and Decimals) round up to
-0.0
rather than0.0
as the OP's mapping requires.
These can all be solved using the Decimal module, though not as prettily as I'd like:
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
def classify(value):
number = Decimal('{:.2f}'.format(value))
if number < 0:
round_method = ROUND_HALF_DOWN
else:
round_method = ROUND_HALF_UP
rounded_number = number.quantize(Decimal('0.1'), rounding=round_method)
if rounded_number == 0.0:
rounded_number = Decimal('0.0')
return r'{}'.format(rounded_number)
Both ROUND_HALF_DOWN and ROUND_HALF_UP are required as ROUND_HALF_UP actually rounds away from zero rather than towards Infinity. .quantize
rounds a Decimal value to the places given by the first argument, and allows us to specify a rounding method.
Bonus: Bisect Breakpoints using range()
For the bisect solutions, this will generate the breakpoints used by the OP:
from decimal import Decimal
breakpoints = [Decimal('{}e-2'.format(e)) for e in range(-85, 96, 10)]
add a comment |
Many of these answers suggest some kind of rounding as a solution. Unfortunately, there are three problems with using rounding for this purpose, and at the time of writing all of them fell prey to at least one.
- Floating point representation of decimal values is inexact. For example, the float
0.85
is in fact0.8499999999999999777955395...
. - round() uses ties-round-to-even, also known as scientific or banker's rounding, rather than the arithmetic rounding many of us learned in school. This means e.g. 0.85 rounds to 0.8 instead of 0.9, and 0.25 rounds to 0.2 instead of 0.3.
- very small negative floats (and Decimals) round up to
-0.0
rather than0.0
as the OP's mapping requires.
These can all be solved using the Decimal module, though not as prettily as I'd like:
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
def classify(value):
number = Decimal('{:.2f}'.format(value))
if number < 0:
round_method = ROUND_HALF_DOWN
else:
round_method = ROUND_HALF_UP
rounded_number = number.quantize(Decimal('0.1'), rounding=round_method)
if rounded_number == 0.0:
rounded_number = Decimal('0.0')
return r'{}'.format(rounded_number)
Both ROUND_HALF_DOWN and ROUND_HALF_UP are required as ROUND_HALF_UP actually rounds away from zero rather than towards Infinity. .quantize
rounds a Decimal value to the places given by the first argument, and allows us to specify a rounding method.
Bonus: Bisect Breakpoints using range()
For the bisect solutions, this will generate the breakpoints used by the OP:
from decimal import Decimal
breakpoints = [Decimal('{}e-2'.format(e)) for e in range(-85, 96, 10)]
add a comment |
Many of these answers suggest some kind of rounding as a solution. Unfortunately, there are three problems with using rounding for this purpose, and at the time of writing all of them fell prey to at least one.
- Floating point representation of decimal values is inexact. For example, the float
0.85
is in fact0.8499999999999999777955395...
. - round() uses ties-round-to-even, also known as scientific or banker's rounding, rather than the arithmetic rounding many of us learned in school. This means e.g. 0.85 rounds to 0.8 instead of 0.9, and 0.25 rounds to 0.2 instead of 0.3.
- very small negative floats (and Decimals) round up to
-0.0
rather than0.0
as the OP's mapping requires.
These can all be solved using the Decimal module, though not as prettily as I'd like:
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
def classify(value):
number = Decimal('{:.2f}'.format(value))
if number < 0:
round_method = ROUND_HALF_DOWN
else:
round_method = ROUND_HALF_UP
rounded_number = number.quantize(Decimal('0.1'), rounding=round_method)
if rounded_number == 0.0:
rounded_number = Decimal('0.0')
return r'{}'.format(rounded_number)
Both ROUND_HALF_DOWN and ROUND_HALF_UP are required as ROUND_HALF_UP actually rounds away from zero rather than towards Infinity. .quantize
rounds a Decimal value to the places given by the first argument, and allows us to specify a rounding method.
Bonus: Bisect Breakpoints using range()
For the bisect solutions, this will generate the breakpoints used by the OP:
from decimal import Decimal
breakpoints = [Decimal('{}e-2'.format(e)) for e in range(-85, 96, 10)]
Many of these answers suggest some kind of rounding as a solution. Unfortunately, there are three problems with using rounding for this purpose, and at the time of writing all of them fell prey to at least one.
- Floating point representation of decimal values is inexact. For example, the float
0.85
is in fact0.8499999999999999777955395...
. - round() uses ties-round-to-even, also known as scientific or banker's rounding, rather than the arithmetic rounding many of us learned in school. This means e.g. 0.85 rounds to 0.8 instead of 0.9, and 0.25 rounds to 0.2 instead of 0.3.
- very small negative floats (and Decimals) round up to
-0.0
rather than0.0
as the OP's mapping requires.
These can all be solved using the Decimal module, though not as prettily as I'd like:
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
def classify(value):
number = Decimal('{:.2f}'.format(value))
if number < 0:
round_method = ROUND_HALF_DOWN
else:
round_method = ROUND_HALF_UP
rounded_number = number.quantize(Decimal('0.1'), rounding=round_method)
if rounded_number == 0.0:
rounded_number = Decimal('0.0')
return r'{}'.format(rounded_number)
Both ROUND_HALF_DOWN and ROUND_HALF_UP are required as ROUND_HALF_UP actually rounds away from zero rather than towards Infinity. .quantize
rounds a Decimal value to the places given by the first argument, and allows us to specify a rounding method.
Bonus: Bisect Breakpoints using range()
For the bisect solutions, this will generate the breakpoints used by the OP:
from decimal import Decimal
breakpoints = [Decimal('{}e-2'.format(e)) for e in range(-85, 96, 10)]
edited Mar 17 at 8:08
answered Mar 17 at 7:28
WlerinWlerin
24219
24219
add a comment |
add a comment |
Try something like this, if you don't like loops:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = [ r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
idx = [value >= end for end in endpts].index(False)
if not idx:
raise ValueError('Value outside of range')
return ts_folder[idx-1]
Of course, the loop is just "hidden" in the list comprehension.
Obviously, in this example, it would be better to generate endpts
and ts_fol
programmatically rather than writing them all out, but you indicated that in the real situation the endpoints and values aren't so straightforward.
This raises a ValueError
if value
≥ 0.95 (because False
is not found in the list comprehension) or if value
< -0.95 (because then idx
is 0); the original version raises a UnboundLocalError
in these cases.
You could also save three lines and skip a few comparisons by doing this:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_fol = [ None, r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
return next((ts for ts, end in zip(ts_fol, endpts) if value < end), None)
This version returns None
rather than raising exceptions for any value outside the bounds.
add a comment |
Try something like this, if you don't like loops:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = [ r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
idx = [value >= end for end in endpts].index(False)
if not idx:
raise ValueError('Value outside of range')
return ts_folder[idx-1]
Of course, the loop is just "hidden" in the list comprehension.
Obviously, in this example, it would be better to generate endpts
and ts_fol
programmatically rather than writing them all out, but you indicated that in the real situation the endpoints and values aren't so straightforward.
This raises a ValueError
if value
≥ 0.95 (because False
is not found in the list comprehension) or if value
< -0.95 (because then idx
is 0); the original version raises a UnboundLocalError
in these cases.
You could also save three lines and skip a few comparisons by doing this:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_fol = [ None, r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
return next((ts for ts, end in zip(ts_fol, endpts) if value < end), None)
This version returns None
rather than raising exceptions for any value outside the bounds.
add a comment |
Try something like this, if you don't like loops:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = [ r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
idx = [value >= end for end in endpts].index(False)
if not idx:
raise ValueError('Value outside of range')
return ts_folder[idx-1]
Of course, the loop is just "hidden" in the list comprehension.
Obviously, in this example, it would be better to generate endpts
and ts_fol
programmatically rather than writing them all out, but you indicated that in the real situation the endpoints and values aren't so straightforward.
This raises a ValueError
if value
≥ 0.95 (because False
is not found in the list comprehension) or if value
< -0.95 (because then idx
is 0); the original version raises a UnboundLocalError
in these cases.
You could also save three lines and skip a few comparisons by doing this:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_fol = [ None, r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
return next((ts for ts, end in zip(ts_fol, endpts) if value < end), None)
This version returns None
rather than raising exceptions for any value outside the bounds.
Try something like this, if you don't like loops:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_folder = [ r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
idx = [value >= end for end in endpts].index(False)
if not idx:
raise ValueError('Value outside of range')
return ts_folder[idx-1]
Of course, the loop is just "hidden" in the list comprehension.
Obviously, in this example, it would be better to generate endpts
and ts_fol
programmatically rather than writing them all out, but you indicated that in the real situation the endpoints and values aren't so straightforward.
This raises a ValueError
if value
≥ 0.95 (because False
is not found in the list comprehension) or if value
< -0.95 (because then idx
is 0); the original version raises a UnboundLocalError
in these cases.
You could also save three lines and skip a few comparisons by doing this:
def classify(value):
endpts = [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
ts_fol = [ None, r'-0.9', r'-0.8', r'-0.7', r'-0.6', r'-0.5', r'-0.4', r'-0.3', r'-0.2', r'-0.1', r'.0', r'.1', r'.2', r'.3', r'.4', r'.5', r'.6', r'.7', r'.8', r'.9']
return next((ts for ts, end in zip(ts_fol, endpts) if value < end), None)
This version returns None
rather than raising exceptions for any value outside the bounds.
edited Mar 15 at 22:10
answered Mar 15 at 21:00
KundorKundor
2,9661325
2,9661325
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55180829%2fpython-if-else-code-style-for-reduced-code-for-rounding-floats%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
That's an example. In my experiment, diffence isn't always 0.5. round() is a good way in this case but not always work for me
– Kuang 鄺世銘
Mar 15 at 11:03
I think this is a case by case though. Think of whenever the pattern is not linear by example.
– Olivier Melançon
Mar 15 at 11:17
8
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
Mar 15 at 12:04
@Kuang鄺世銘 You might want to check out Python's Philosophy and Sentdex's Python3 Playlist.
– LogicalBranch
Mar 15 at 13:48
7
It's a great way to hide bugs!
ts_folder
is undefined for values larger than0.95
or smaller than-0.85
. Also, the result for-0.45
and-0.35
differ by0.2
.– Eric Duminil
Mar 15 at 17:34