Python if-else code style for reduced code for rounding floats
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 2 more comments
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
48
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
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 鄺世銘
15 hours ago
7
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
14 hours ago
4
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
8 hours ago
|
show 2 more comments
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 3 hours ago
smci
15.3k677108
15.3k677108
asked 15 hours ago
Kuang 鄺世銘Kuang 鄺世銘
826
826
48
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
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 鄺世銘
15 hours ago
7
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
14 hours ago
4
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
8 hours ago
|
show 2 more comments
48
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
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 鄺世銘
15 hours ago
7
At the very least, use chained comparisons:-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95
– chepner
14 hours ago
4
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
8 hours ago
48
48
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
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 鄺世銘
15 hours ago
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 鄺世銘
15 hours ago
7
7
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
14 hours ago
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
14 hours ago
4
4
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
8 hours ago
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
8 hours ago
|
show 2 more comments
11 Answers
11
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.
1
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
10 hours ago
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
10 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
6 hours ago
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 |
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
15
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
13 hours ago
1
@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
11 hours ago
add a comment |
Why not just use the round()
built-in ?
ts_folder = "\" + str(round(value + 1e-16, 1))
(The +1e-16
etc is because floats like .15
are by default rounded to .1
, not .2
)
More on round()
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
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
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
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
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
8 hours ago
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
15 hours ago
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 |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
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
11 Answers
11
active
oldest
votes
11 Answers
11
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.
1
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
10 hours ago
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
10 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
6 hours ago
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.
1
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
10 hours ago
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
10 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
6 hours ago
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.
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.
edited 6 hours ago
answered 15 hours ago
Olivier MelançonOlivier Melançon
13.9k22042
13.9k22042
1
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
10 hours ago
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
10 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
6 hours ago
add a comment |
1
Note that the OP hadif lo <=key < hi
.
– Martin Bonner
10 hours ago
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
10 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with thebisect
module would be a good option.
– user2357112
6 hours ago
1
1
Note that the OP had
if lo <=key < hi
.– Martin Bonner
10 hours ago
Note that the OP had
if lo <=key < hi
.– Martin Bonner
10 hours ago
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
10 hours ago
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
10 hours ago
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
8 hours ago
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
8 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
@EricDuminill yes, that is why I suggest providing them as literals, without using a list-comprehension.
– Olivier Melançon
7 hours ago
Assuming the intervals partition a range, binary search with the
bisect
module would be a good option.– user2357112
6 hours ago
Assuming the intervals partition a range, binary search with the
bisect
module would be a good option.– user2357112
6 hours ago
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 8 hours ago
PeterPeter
11.5k1846
11.5k1846
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
15
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
13 hours ago
1
@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
11 hours ago
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
15
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
13 hours ago
1
@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
11 hours ago
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
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 11 hours ago
chepner
257k34247339
257k34247339
answered 14 hours ago
Hirabayashi TaroHirabayashi Taro
533311
533311
15
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
13 hours ago
1
@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
11 hours ago
add a comment |
15
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
13 hours ago
1
@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
11 hours ago
15
15
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
13 hours ago
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
13 hours ago
1
1
@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
11 hours ago
@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
11 hours ago
add a comment |
Why not just use the round()
built-in ?
ts_folder = "\" + str(round(value + 1e-16, 1))
(The +1e-16
etc is because floats like .15
are by default rounded to .1
, not .2
)
More on round()
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
add a comment |
Why not just use the round()
built-in ?
ts_folder = "\" + str(round(value + 1e-16, 1))
(The +1e-16
etc is because floats like .15
are by default rounded to .1
, not .2
)
More on round()
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
add a comment |
Why not just use the round()
built-in ?
ts_folder = "\" + str(round(value + 1e-16, 1))
(The +1e-16
etc is because floats like .15
are by default rounded to .1
, not .2
)
More on round()
Why not just use the round()
built-in ?
ts_folder = "\" + str(round(value + 1e-16, 1))
(The +1e-16
etc is because floats like .15
are by default rounded to .1
, not .2
)
More on round()
edited 8 hours ago
answered 15 hours ago
FukiyelFukiyel
904216
904216
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
add a comment |
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
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
8 hours ago
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
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
Oh my bad, should be good now
– Fukiyel
8 hours ago
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
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
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
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
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 9 hours ago
Eric Towers
3,6191015
3,6191015
answered 10 hours ago
Martin BonnerMartin Bonner
23.6k33266
23.6k33266
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
add a comment |
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
1
1
this is one of the most useful answers here
– aaaaaa
3 hours ago
this is one of the most useful answers here
– aaaaaa
3 hours ago
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
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
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 15 hours ago
JerilJeril
2,4411735
2,4411735
add a comment |
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
8 hours ago
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
8 hours ago
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 13 hours ago
MehrdadMehrdad
129k90415758
129k90415758
You need to start at ` i = -95`, though.
– Eric Duminil
8 hours ago
add a comment |
You need to start at ` i = -95`, though.
– Eric Duminil
8 hours ago
You need to start at ` i = -95`, though.
– Eric Duminil
8 hours ago
You need to start at ` i = -95`, though.
– Eric Duminil
8 hours ago
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
15 hours ago
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
15 hours ago
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 6 hours ago
Olivier Melançon
13.9k22042
13.9k22042
answered 15 hours ago
MelKoutchMelKoutch
555
555
2
Please try and answer with a clear example
– AJS
15 hours ago
add a comment |
2
Please try and answer with a clear example
– AJS
15 hours ago
2
2
Please try and answer with a clear example
– AJS
15 hours ago
Please try and answer with a clear example
– AJS
15 hours ago
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 9 hours ago
AcccumulationAcccumulation
1,42329
1,42329
add a comment |
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
add a comment |
from decimal import Decimal
def classify(value):
number = Decimal(value)
result = "%.2f" % (number)
return Decimal(round(float(result), 2))
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
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 7 hours ago
wizzwizz4
3,60011637
3,60011637
answered 15 hours ago
Asif AkhtarAsif Akhtar
76110
76110
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
add a comment |
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
4
4
star imports are bad practice.
– bruno desthuilliers
13 hours ago
star imports are bad practice.
– bruno desthuilliers
13 hours ago
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 3 hours ago
answered 5 hours ago
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
48
That is the longest code for a rounding function I've ever seen!
– Aran-Fey
15 hours ago
I agree with @Aran-Fey. That thing's huge, OP!
– connectyourcharger
15 hours ago
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 鄺世銘
15 hours ago
7
At the very least, use chained comparisons:
-0.95 <= value < -0.85
instead of `value < -0.85 and value >= -0.95– chepner
14 hours ago
4
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
8 hours ago