Python - rounding to the nearest integer - python

I've been trying to figure this out for ages but with no luck. I need to round each coefficient to the nearest integer.
To do this I introduce a small number dq=0.0001. If a[0]=0.5, then round(a[0]) = 0.0; this is clearly not good. However, to work around this introduce dq: round(a[0]+dq) = round(0.5001) = 1.0; this is good.
For negatives such as a[2] = -0.5: round(a[2]-dq) = round(-0.5001) = -1.0. I take into account the negatives using if statements. This is my code which does not return the right value:
a= 0.5,0.5,-0.5,-0.5
dq = 0.0001 #small number
b = round(a[0]+dq), round(a[1]+dq), round(a[2]+dq), round(a[3]+dq)
if a[0] < 0:
b[0] == round(a[0]-dq)
if a[1] < 0:
b[1] == round(a[1]-dq)
if a[2] < 0:
b[2] == round(a[2]-dq)
if a[3] < 0:
b[3] == round(a[3]-dq)
print(b)
Output:
(1,1,0,0)
Clearly it is the if statements that are dysfunctional.
P.s. and I need to be able to use the resulting b in later calculations.

perhaps you could just use something like
a = [0.5, 0.5, -0.5, -0.5]
print(list(map(lambda v: int(round(v + (dq if v > 0 else -dq))), a)))
this yields
[1, 1, -1, -1]
However, an alternative would be:
x = 0.49999 #just slightly less than 0.5
round(x)
>>> 0.0
#first round to 2 digits and then to an "integer"
round(round(x, 2))
>>> 1.0

You can probably do it in a smarter way, but here's a quick solution to your problem. First of all, use a for loop to round the elements of a. a is a tuple, by the way, so you could loop through its elements.
Second of all, your logic is at fault above. If an element of a is positive, i.e. > 0, you should add the dq instead of subtracting, as you mentioned yourself.
In [53]: for i in a:
...: if i < 0:
...: print(round(i-dq))
...: else:
...: print(round(i+dq))
...:
...:
1.0
1.0
-1.0
-1.0

If I understand your problem correctly, you'd like negative numbers to be rounded down to the nearest integer and positive numbers to be rounded up to the nearest integer. If that's the case, you could use math.ceil and math.floor:
import math
def my_round(x):
if x < 0:
return math.floor(x)
if x >= 0:
return math.ceil(x)
my_round(0.5) # 1
my_round(-0.5) # -1

Related

How to run for loop on float variables in python? [duplicate]

Is there a range() equivalent for floats in Python?
>>> range(0.5,5,1.5)
[0, 1, 2, 3, 4]
>>> range(0.5,5,0.5)
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
range(0.5,5,0.5)
ValueError: range() step argument must not be zero
You can either use:
[x / 10.0 for x in range(5, 50, 15)]
or use lambda / map:
map(lambda x: x/10.0, range(5, 50, 15))
I don't know a built-in function, but writing one like [this](https://stackoverflow.com/a/477610/623735) shouldn't be too complicated.
def frange(x, y, jump):
while x < y:
yield x
x += jump
---
As the comments mention, this could produce unpredictable results like:
>>> list(frange(0, 100, 0.1))[-1]
99.9999999999986
To get the expected result, you can use one of the other answers in this question, or as #Tadhg mentioned, you can use decimal.Decimal as the jump argument. Make sure to initialize it with a string rather than a float.
>>> import decimal
>>> list(frange(0, 100, decimal.Decimal('0.1')))[-1]
Decimal('99.9')
Or even:
import decimal
def drange(x, y, jump):
while x < y:
yield float(x)
x += decimal.Decimal(jump)
And then:
>>> list(drange(0, 100, '0.1'))[-1]
99.9
[editor's not: if you only use positive jump and integer start and stop (x and y) , this works fine. For a more general solution see here.]
I used to use numpy.arange but had some complications controlling the number of elements it returns, due to floating point errors. So now I use linspace, e.g.:
>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([ 0. , 3.33333333, 6.66666667, 10. ])
Pylab has frange (a wrapper, actually, for matplotlib.mlab.frange):
>>> import pylab as pl
>>> pl.frange(0.5,5,0.5)
array([ 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. ])
Eagerly evaluated (2.x range):
[x * .5 for x in range(10)]
Lazily evaluated (2.x xrange, 3.x range):
itertools.imap(lambda x: x * .5, xrange(10)) # or range(10) as appropriate
Alternately:
itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10)
# without applying the `islice`, we get an infinite stream of half-integers.
using itertools: lazily evaluated floating point range:
>>> from itertools import count, takewhile
>>> def frange(start, stop, step):
return takewhile(lambda x: x< stop, count(start, step))
>>> list(frange(0.5, 5, 1.5))
# [0.5, 2.0, 3.5]
I helped add the function numeric_range to the package more-itertools.
more_itertools.numeric_range(start, stop, step) acts like the built in function range but can handle floats, Decimal, and Fraction types.
>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)
There is no such built-in function, but you can use the following (Python 3 code) to do the job as safe as Python allows you to.
from fractions import Fraction
def frange(start, stop, jump, end=False, via_str=False):
"""
Equivalent of Python 3 range for decimal numbers.
Notice that, because of arithmetic errors, it is safest to
pass the arguments as strings, so they can be interpreted to exact fractions.
>>> assert Fraction('1.1') - Fraction(11, 10) == 0.0
>>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)
Parameter `via_str` can be set to True to transform inputs in strings and then to fractions.
When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long
as approximation happens beyond the decimal digits that Python uses for printing.
For example, in the case of 0.1, this is the case:
>>> assert str(0.1) == '0.1'
>>> assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'
If you are not sure whether your decimal inputs all have this property, you are better off
passing them as strings. String representations can be in integer, decimal, exponential or
even fraction notation.
>>> assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
>>> assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
>>> assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
>>> assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0
"""
if via_str:
start = str(start)
stop = str(stop)
jump = str(jump)
start = Fraction(start)
stop = Fraction(stop)
jump = Fraction(jump)
while start < stop:
yield float(start)
start += jump
if end and start == stop:
yield(float(start))
You can verify all of it by running a few assertions:
assert Fraction('1.1') - Fraction(11, 10) == 0.0
assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)
assert str(0.1) == '0.1'
assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'
assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0
assert list(frange(2, 3, '1/6', end=True))[-1] == 3.0
assert list(frange(0, 100, '1/3', end=True))[-1] == 100.0
Code available on GitHub
As kichik wrote, this shouldn't be too complicated. However this code:
def frange(x, y, jump):
while x < y:
yield x
x += jump
Is inappropriate because of the cumulative effect of errors when working with floats.
That is why you receive something like:
>>>list(frange(0, 100, 0.1))[-1]
99.9999999999986
While the expected behavior would be:
>>>list(frange(0, 100, 0.1))[-1]
99.9
Solution 1
The cumulative error can simply be reduced by using an index variable. Here's the example:
from math import ceil
def frange2(start, stop, step):
n_items = int(ceil((stop - start) / step))
return (start + i*step for i in range(n_items))
This example works as expected.
Solution 2
No nested functions. Only a while and a counter variable:
def frange3(start, stop, step):
res, n = start, 1
while res < stop:
yield res
res = start + n * step
n += 1
This function will work well too, except for the cases when you want the reversed range. E.g:
>>>list(frange3(1, 0, -.1))
[]
Solution 1 in this case will work as expected. To make this function work in such situations, you must apply a hack, similar to the following:
from operator import gt, lt
def frange3(start, stop, step):
res, n = start, 0.
predicate = lt if start < stop else gt
while predicate(res, stop):
yield res
res = start + n * step
n += 1
With this hack you can use these functions with negative steps:
>>>list(frange3(1, 0, -.1))
[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3999999999999999, 0.29999999999999993, 0.19999999999999996, 0.09999999999999998]
Solution 3
You can go even further with plain standard library and compose a range function for the most of numeric types:
from itertools import count
from itertools import takewhile
def any_range(start, stop, step):
start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))
This generator is adapted from the Fluent Python book (Chapter 14. Iterables, Iterators and generators). It will not work with decreasing ranges. You must apply a hack, like in the previous solution.
You can use this generator as follows, for example:
>>>list(any_range(Fraction(2, 1), Fraction(100, 1), Fraction(1, 3)))[-1]
299/3
>>>list(any_range(Decimal('2.'), Decimal('4.'), Decimal('.3')))
[Decimal('2'), Decimal('2.3'), Decimal('2.6'), Decimal('2.9'), Decimal('3.2'), Decimal('3.5'), Decimal('3.8')]
And of course you can use it with float and int as well.
Be careful
If you want to use these functions with negative steps, you should add a check for the step sign, e.g.:
no_proceed = (start < stop and step < 0) or (start > stop and step > 0)
if no_proceed: raise StopIteration
The best option here is to raise StopIteration, if you want to mimic the range function itself.
Mimic range
If you would like to mimic the range function interface, you can provide some argument checks:
def any_range2(*args):
if len(args) == 1:
start, stop, step = 0, args[0], 1.
elif len(args) == 2:
start, stop, step = args[0], args[1], 1.
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('any_range2() requires 1-3 numeric arguments')
# here you can check for isinstance numbers.Real or use more specific ABC or whatever ...
start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))
I think, you've got the point. You can go with any of these functions (except the very first one) and all you need for them is python standard library.
Why Is There No Floating Point Range Implementation In The Standard Library?
As made clear by all the posts here, there is no floating point version of range(). That said, the omission makes sense if we consider that the range() function is often used as an index (and of course, that means an accessor) generator. So, when we call range(0,40), we're in effect saying we want 40 values starting at 0, up to 40, but non-inclusive of 40 itself.
When we consider that index generation is as much about the number of indices as it is their values, the use of a float implementation of range() in the standard library makes less sense. For example, if we called the function frange(0, 10, 0.25), we would expect both 0 and 10 to be included, but that would yield a generator with 41 values, not the 40 one might expect from 10/0.25.
Thus, depending on its use, an frange() function will always exhibit counter intuitive behavior; it either has too many values as perceived from the indexing perspective or is not inclusive of a number that reasonably should be returned from the mathematical perspective. In other words, it's easy to see how such a function would appear to conflate two very different use cases – the naming implies the indexing use case; the behavior implies a mathematical one.
The Mathematical Use Case
With that said, as discussed in other posts, numpy.linspace() performs the generation from the mathematical perspective nicely:
numpy.linspace(0, 10, 41)
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75,
2. , 2.25, 2.5 , 2.75, 3. , 3.25, 3.5 , 3.75,
4. , 4.25, 4.5 , 4.75, 5. , 5.25, 5.5 , 5.75,
6. , 6.25, 6.5 , 6.75, 7. , 7.25, 7.5 , 7.75,
8. , 8.25, 8.5 , 8.75, 9. , 9.25, 9.5 , 9.75, 10.
])
The Indexing Use Case
And for the indexing perspective, I've written a slightly different approach with some tricksy string magic that allows us to specify the number of decimal places.
# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield float(("%0." + str(decimals) + "f") % (i * skip))
Similarly, we can also use the built-in round function and specify the number of decimals:
# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield round(i * skip, ndigits = decimals)
A Quick Comparison & Performance
Of course, given the above discussion, these functions have a fairly limited use case. Nonetheless, here's a quick comparison:
def compare_methods (start, stop, skip):
string_test = frange_S(start, stop, skip)
round_test = frange_R(start, stop, skip)
for s, r in zip(string_test, round_test):
print(s, r)
compare_methods(-2, 10, 1/3)
The results are identical for each:
-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67
And some timings:
>>> import timeit
>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """
>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115
>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166
Looks like the string formatting method wins by a hair on my system.
The Limitations
And finally, a demonstration of the point from the discussion above and one last limitation:
# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
print(x)
0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75
Further, when the skip parameter is not divisible by the stop value, there can be a yawning gap given the latter issue:
# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
print(x)
0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43
There are ways to address this issue, but at the end of the day, the best approach would probably be to just use Numpy.
A solution without numpy etc dependencies was provided by kichik but due to the floating point arithmetics, it often behaves unexpectedly. As noted by me and blubberdiblub, additional elements easily sneak into the result. For example naive_frange(0.0, 1.0, 0.1) would yield 0.999... as its last value and thus yield 11 values in total.
A bit more robust version is provided here:
def frange(x, y, jump=1.0):
'''Range for floats.'''
i = 0.0
x = float(x) # Prevent yielding integers.
x0 = x
epsilon = jump / 2.0
yield x # yield always first value
while x + epsilon < y:
i += 1.0
x = x0 + i * jump
if x < y:
yield x
Because the multiplication, the rounding errors do not accumulate. The use of epsilon takes care of possible rounding error of the multiplication, even though issues of course might rise in the very small and very large ends. Now, as expected:
> a = list(frange(0.0, 1.0, 0.1))
> a[-1]
0.9
> len(a)
10
And with somewhat larger numbers:
> b = list(frange(0.0, 1000000.0, 0.1))
> b[-1]
999999.9
> len(b)
10000000
The code is also available as a GitHub Gist.
This can be done with numpy.arange(start, stop, stepsize)
import numpy as np
np.arange(0.5,5,1.5)
>> [0.5, 2.0, 3.5, 5.0]
# OBS you will sometimes see stuff like this happening,
# so you need to decide whether that's not an issue for you, or how you are going to catch it.
>> [0.50000001, 2.0, 3.5, 5.0]
Note 1:
From the discussion in the comment section here, "never use numpy.arange() (the numpy documentation itself recommends against it). Use numpy.linspace as recommended by wim, or one of the other suggestions in this answer"
Note 2:
I have read the discussion in a few comments here, but after coming back to this question for the third time now, I feel this information should be placed in a more readable position.
A simpler library-less version
Aw, heck -- I'll toss in a simple library-less version. Feel free to improve on it[*]:
def frange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
dy = stop-start
# f(i) goes from start to stop as i goes from 0 to nsteps
return [start + float(i)*dy/nsteps for i in range(nsteps)]
The core idea is that nsteps is the number of steps to get you from start to stop and range(nsteps) always emits integers so there's no loss of accuracy. The final step is to map [0..nsteps] linearly onto [start..stop].
edit
If, like alancalvitti you'd like the series to have exact rational representation, you can always use Fractions:
from fractions import Fraction
def rrange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
return [Fraction(i, nsteps) for i in range(nsteps)]
[*] In particular, frange() returns a list, not a generator. But it sufficed for my needs.
Usage
# Counting up
drange(0, 0.4, 0.1)
[0, 0.1, 0.2, 0.30000000000000004, 0.4]
# Counting down
drange(0, -0.4, -0.1)
[0, -0.1, -0.2, -0.30000000000000004, -0.4]
To round each step to N decimal places
drange(0, 0.4, 0.1, round_decimal_places=4)
[0, 0.1, 0.2, 0.3, 0.4]
drange(0, -0.4, -0.1, round_decimal_places=4)
[0, -0.1, -0.2, -0.3, -0.4]
Code
def drange(start, end, increment, round_decimal_places=None):
result = []
if start < end:
# Counting up, e.g. 0 to 0.4 in 0.1 increments.
if increment < 0:
raise Exception("Error: When counting up, increment must be positive.")
while start <= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
else:
# Counting down, e.g. 0 to -0.4 in -0.1 increments.
if increment > 0:
raise Exception("Error: When counting down, increment must be negative.")
while start >= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
return result
Why choose this answer?
Many other answers will hang when asked to count down.
Many other answers will give incorrectly rounded results.
Other answers based on np.linspace are hit-and-miss, they may or may not work due to difficulty in choosing the correct number of divisions. np.linspace really struggles with decimal increments of 0.1, and the order of divisions in the formula to convert the increment into a number of splits can result in either correct or broken code.
Other answers based on np.arange are deprecated.
If in doubt, try the four tests cases above.
I do not know if the question is old but there is a arange function in the NumPy library, it could work as a range.
np.arange(0,1,0.1)
#out:
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
i wrote a function that returns a tuple of a range of double precision floating point numbers without any decimal places beyond the hundredths. it was simply a matter of parsing the range values like strings and splitting off the excess. I use it for displaying ranges to select from within a UI. I hope someone else finds it useful.
def drange(start,stop,step):
double_value_range = []
while start<stop:
a = str(start)
a.split('.')[1].split('0')[0]
start = float(str(a))
double_value_range.append(start)
start = start+step
double_value_range_tuple = tuple(double_value_range)
#print double_value_range_tuple
return double_value_range_tuple
Whereas integer-based ranges are well defined in that "what you see is what you get", there are things that are not readily seen in floats that cause troubles in getting what appears to be a well defined behavior in a desired range.
There are two approaches that one can take:
split a given range into a certain number of segment: the linspace approach in which you accept the large number of decimal digits when you select a number of points that does not divide the span well (e.g. 0 to 1 in 7 steps will give a first step value of 0.14285714285714285)
give the desired WYSIWIG step size that you already know should work and wish that it would work. Your hopes will often be dashed by getting values that miss the end point that you wanted to hit.
Multiples can be higher or lower than you expect:
>>> 3*.1 > .3 # 0.30000000000000004
True
>>> 3*.3 < 0.9 # 0.8999999999999999
True
You will try to avoid accumulating errors by adding multiples of your step and not incrementing, but the problem will always present itself and you just won't get what you expect if you did it by hand on paper -- with exact decimals. But you know it should be possible since Python shows you 0.1 instead of the underlying integer ratio having a close approximation to 0.1:
>>> (3*.1).as_integer_ratio()
(1351079888211149, 4503599627370496)
In the methods offered as answers, the use of Fraction here with the option to handle input as strings is best. I have a few suggestions to make it better:
make it handle range-like defaults so you can start from 0 automatically
make it handle decreasing ranges
make the output look like you would expect if you were using exact arithmetic
I offer a routine that does these same sort of thing but which does not use the Fraction object. Instead, it uses round to create numbers having the same apparent digits as the numbers would have if you printed them with python, e.g. 1 decimal for something like 0.1 and 3 decimals for something like 0.004:
def frange(start, stop, step, n=None):
"""return a WYSIWYG series of float values that mimic range behavior
by excluding the end point and not printing extraneous digits beyond
the precision of the input numbers (controlled by n and automatically
detected based on the string representation of the numbers passed).
EXAMPLES
========
non-WYSIWYS simple list-comprehension
>>> [.11 + i*.1 for i in range(3)]
[0.11, 0.21000000000000002, 0.31]
WYSIWYG result for increasing sequence
>>> list(frange(0.11, .33, .1))
[0.11, 0.21, 0.31]
and decreasing sequences
>>> list(frange(.345, .1, -.1))
[0.345, 0.245, 0.145]
To hit the end point for a sequence that is divisibe by
the step size, make the end point a little bigger by
adding half the step size:
>>> dx = .2
>>> list(frange(0, 1 + dx/2, dx))
[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
"""
if step == 0:
raise ValueError('step must not be 0')
# how many decimal places are showing?
if n is None:
n = max([0 if '.' not in str(i) else len(str(i).split('.')[1])
for i in (start, stop, step)])
if step*(stop - start) > 0: # a non-null incr/decr range
if step < 0:
for i in frange(-start, -stop, -step, n):
yield -i
else:
steps = round((stop - start)/step)
while round(step*steps + start, n) < stop:
steps += 1
for i in range(steps):
yield round(start + i*step, n)
def Range(*argSequence):
if len(argSequence) == 3:
imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2]
i = imin; iList = []
while i <= imax:
iList.append(i)
i += di
return iList
if len(argSequence) == 2:
return Range(argSequence[0], argSequence[1], 1)
if len(argSequence) == 1:
return Range(1, argSequence[0], 1)
Please note the first letter of Range is capital. This naming method is not encouraged for functions in Python. You can change Range to something like drange or frange if you want. The "Range" function behaves just as you want it to. You can check it's manual here [ http://reference.wolfram.com/language/ref/Range.html ].
I think that there is a very simple answer that really emulates all the features of range but for both float and integer. In this solution, you just suppose that your approximation by default is 1e-7 (or the one you choose) and you can change it when you call the function.
def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default
'''
This function is equivalent to range but for both float and integer
'''
if not stop: # If there is no y value: range(x)
stop= start
start= 0
valor= round(start,approx)
while valor < stop:
if valor==int(valor):
yield int(round(valor,approx))
else:
yield float(round(valor,approx))
valor += jump
for i in drange(12):
print(i)
Talk about making a mountain out of a mole hill.
If you relax the requirement to make a float analog of the range function, and just create a list of floats that is easy to use in a for loop, the coding is simple and robust.
def super_range(first_value, last_value, number_steps):
if not isinstance(number_steps, int):
raise TypeError("The value of 'number_steps' is not an integer.")
if number_steps < 1:
raise ValueError("Your 'number_steps' is less than 1.")
step_size = (last_value-first_value)/(number_steps-1)
output_list = []
for i in range(number_steps):
output_list.append(first_value + step_size*i)
return output_list
first = 20.0
last = -50.0
steps = 5
print(super_range(first, last, steps))
The output will be
[20.0, 2.5, -15.0, -32.5, -50.0]
Note that the function super_range is not limited to floats. It can handle any data type for which the operators +, -, *, and / are defined, such as complex, Decimal, and numpy.array:
import cmath
first = complex(1,2)
last = complex(5,6)
steps = 5
print(super_range(first, last, steps))
from decimal import *
first = Decimal(20)
last = Decimal(-50)
steps = 5
print(super_range(first, last, steps))
import numpy as np
first = np.array([[1, 2],[3, 4]])
last = np.array([[5, 6],[7, 8]])
steps = 5
print(super_range(first, last, steps))
The output will be:
[(1+2j), (2+3j), (3+4j), (4+5j), (5+6j)]
[Decimal('20.0'), Decimal('2.5'), Decimal('-15.0'), Decimal('-32.5'), Decimal('-50.0')]
[array([[1., 2.],[3., 4.]]),
array([[2., 3.],[4., 5.]]),
array([[3., 4.],[5., 6.]]),
array([[4., 5.],[6., 7.]]),
array([[5., 6.],[7., 8.]])]
There will be of course some rounding errors, so this is not perfect, but this is what I use generally for applications, which don't require high precision. If you wanted to make this more accurate, you could add an extra argument to specify how to handle rounding errors. Perhaps passing a rounding function might make this extensible and allow the programmer to specify how to handle rounding errors.
arange = lambda start, stop, step: [i + step * i for i in range(int((stop - start) / step))]
If I write:
arange(0, 1, 0.1)
It will output:
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]
Is there a range() equivalent for floats in Python?
NO
Use this:
def f_range(start, end, step, coef=0.01):
a = range(int(start/coef), int(end/coef), int(step/coef))
var = []
for item in a:
var.append(item*coef)
return var
There several answers here that don't handle simple edge cases like negative step, wrong start, stop etc. Here's the version that handles many of these cases correctly giving same behaviour as native range():
def frange(start, stop=None, step=1):
if stop is None:
start, stop = 0, start
steps = int((stop-start)/step)
for i in range(steps):
yield start
start += step
Note that this would error out step=0 just like native range. One difference is that native range returns object that is indexable and reversible while above doesn't.
You can play with this code and test cases here.

How To Find Division Without Using Division Operator In Python?

I've Found How to do it for normal numbers which gives quotient above 1
How to find value for which we will get quotient below 1
For example if 28 divided by 500 it gives 0.056
How to implement above example without using division operator
There is not a way to do this, unless you count reciprocals. However, these are fractions which means they use divide.
For example:
>>> 30 / 5 # original calculation
6
>>> 30 * 0.2 # using the reciprocal of 5 – but no division sign visible!
6
However, this option clearly uses division. If you don't see it, here's how:
def divide(x, y):
return x * (1/y)
Because to make a number a reciprocal, you have to divide. But take into consideration that every whole number uses divide: 5 = 5/1, so it's just the same with a different denominator...
#in this case 18/2
e=18
t=2
o=0
l=t
count=0
t=0
for i in range(0,e):
t+=l
count+=1
if t==e:
o=1
print(count)
break
if (t+l) > e:
break
if o!=1:
c=e-t
print(count,'reminder of',c)
You indicate you already have something without division that handles cases with quotients greater than 1. If so, you can reduce your as yet unsolved case like 28/500 by doing 2800/500 and then getting around dividing by 100 (at least directly) by adding two decimal places. Since you want two significant figures, it seems, you actually would want to do 28000/500, at least if you did things as I am guessing you did.
Here's how that plays out in code:
def algorithm_you_already_have(a, b):
"""I assume a>b>0 you can add logic to handle negatives or check for b == 0
you say you already have something that works if a divided by b is greater than 1
Here is one thing you might have done, repeated subtraction"""
assert a > b # this was only working and for use when quotient was greater than 1, you said
assert b > 0 # cannot divide by 0, and not dealing with negatives here, could higher up
result = 0
while a >= b:
a -= b
result += 1
if a + a >= b: # round up if remainder is at least half of divisor
result += 1
return result
# assumes positives, you can add cases as needed
def algorithm_you_need(a, b):
assert a > 0
assert b > 0
temp_result = 0
if a == b:
return 1
if a > b:
return algorithm_you_already_have(a, b)
# still here, then a < b, which is the new case you need
places_shifted = 0
while a < b:
a *= 10
places_shifted += 1
if a == b:
temp_result = 1
else:
temp_result = algorithm_you_already_have(10 * a, b) # times 10 to get a second figure as desired
# but do not count is as a shift, because you are putting the next figure, if any, beyond the first
# process as string
temp_result = str(temp_result)
while places_shifted > 1:
temp_result = '0' + temp_result
places_shifted -= 1
temp_result ="0." + temp_result
return float(temp_result)
print(algorithm_you_need(28,500))

Random rounding to integer in Python

I am looking for a way to round a floating point number up or down to the next integer based on a probability derived from the numbers after the decimal point. For example the floating number 6.1 can be rounded to 6 and to 7. The probability for beeing rounded to 7 is 0.1 and the probability to be rounded to 6 is 1-0.1. So if I run this rounding experiment infinite times, the average of all integer results should be 6.1 again. I don't know if there is a name for such a procedure and if there is already and implemented function in Python.
Of course it'd be very nice if it is possible to round also to e.g. 2 decimal places the same way.
Does that make sense? Any ideas?
Here is a nice one-liner for this. By using the floor function, it will only be rounded up if the random number between 0 and 1 is enough to bring it up to the next highest integer. This method also works with positive and negative numbers equally well.
def probabilistic_round(x):
return int(math.floor(x + random.random()))
Consider the case of a negative input x = -2.25. 75% of the time the random number will be greater than or equal to 0.25 in which case the floor function will result in -2 being the answer. The other 25% of time the number will get rounded down to -3.
To round to different decimal places it can be modified as follows:
def probabilistic_round(x, decimal_places=0):
factor = 10.0**decimal_places
return int(math.floor(x*factor + random.random()))/factor
The probability you're looking for is x-int(x).
To sample with this probability, do random.random() < x-int(x)
import random
import math
import numpy as np
def prob_round(x):
sign = np.sign(x)
x = abs(x)
is_up = random.random() < x-int(x)
round_func = math.ceil if is_up else math.floor
return sign * round_func(x)
x = 6.1
sum( prob_round(x) for i in range(100) ) / 100.
=> 6.12
EDIT: adding an optional prec argument:
def prob_round(x, prec = 0):
fixup = np.sign(x) * 10**prec
x *= fixup
is_up = random.random() < x-int(x)
round_func = math.ceil if is_up else math.floor
return round_func(x) / fixup
x = 8.33333333
[ prob_round(x, prec = 2) for i in range(10) ]
=> [8.3399999999999999,
8.3300000000000001,
8.3399999999999999,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3300000000000001,
8.3399999999999999,
8.3399999999999999]
The most succinct way to do this for non-negative x is:
int(x + random.random())
If for example x == 6.1, then there's a 10% chance that random.random() will be large enough to make x + random.random() >= 7.
Note that if x == 6, then this expression is guaranteed to return 6, because random.random() is always in the range [0, 1).
Update: This method only works for non-negative inputs. For a solution that works for negative numbers, see Chris Locke's answer.
For rounding positive values to integers, you can do this very concisely:
x = int(x) + (random.random() < x - int(x))
This works because Python's bool type is a subclass of int. The value True is equal to 1 and False is equal to 0.
I also came up with a solution based on the binomial function of random and the code already provided by shx2:
def prob_round(x, prec = 0):
fixup = np.sign(x) * 10**prec
x *= fixup
round_func = int(x) + np.random.binomial(1,x-int(x))
return round_func/fixup
Here's an easy way:
x = round(random.random()*100)
The *100 bit means 1 to 100.
If *200, it means 1 to 200.

Python code translation issue or language difference in random number generation

I'm new to Python and have no experience with QBasic. I was running a simulation in Python that came up with theoretically wrong values. I then ran it in QBasic and came up with theoretically predicted values.
Here are the test cases. I'm only counting the probability P(0.9<%Y<=1.8) so the count has to fall within those values. The 1-random.random() was only for that case, when I tried using that for all the cases they still came up with the wrong values. Here's the theoretical outcomes and you can see how it's different:
y~u(0,1)
= 0.575
y~exp(2)
= 0.3371
x1~u(0,1)
x2~u(0,2)
= 0.4475
P(y=0.25)=0.8
P(y=1.5)=0.2
= 0.32
In Python the simulation code is:
def test():
x1,x2,c = 0.0,0.0,0.0
for i in range(10000):
if random.random()< 0.8:
x1 += 0.25
else:
x2 += 1.5
y = x1 + x2
if y>0.9 and y<=1.8:
c = c + 1
return x1,x2,c
print "test: ",test()
def sim(a,b):
#pyab1 = sum([a for a in a if a>0.9 and a<=1.8])/10000
#pyab2 = sum([b for b in b if b>0.9 and b<=1.8])/10000
#print "*****",float(pyab1+pyab2)
#print a+b
#array1 = [[a],[b]]
array1 = a+b
#array1.extend(a)
#array1.extend(b)
#c = 0
#for y in array1:
#if y>0.9 and y<=1.8:
#c = c + 1
pyab = sum([y for y in array1 if y>0.9 and y<=1.8])/10000
print("P(a < x <= b) : {0:8.4f}".format(pyab))
Here's the Python output followed by the values it's supposed to give, but this shows how far off the results are.
case 1: P(a < x <= b) : 0.7169 #should be 0.575
case 2: P(a < x <= b) : 0.4282 #should be 0.3371
case 3: P(a < x <= b) : 0.5966 #should be 0.4475
case 4: P(a < x <= b) : 0.5595 #should be 0.32
In QBasic the simulation code is:
Case 1:
RANDOMIZE
FOR i = 1 TO 10000
X1 = RND(1)
X2 = RND(1)
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 2:
RANDOMIZE
FOR i = 1 TO 10000
X1 = (-0.5)*(LOG(1-RND(1)))
X2 = (-0.5)*(LOG(1-RND(1)))
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 3:
RANDOMIZE
FOR i = 1 TO 10000
X1 = RND(1)
X2 = RND(1)*2
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 4:
RANDOMIZE
FOR i = 1 TO 10000
X14 = RND(1)
X24 = RND(1)
IF (X14<0.8) THEN X41=0.25 ELSE X41=1.5
IF (X24<0.8) THEN X42=0.25 ELSE X42=1.5
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Here's the QBasic output, which shows how this is actually getting the right results.
case 1: P(a < x <= b) : 0.5715
case 2: P(a < x <= b) : 0.3371
case 3: P(a < x <= b) : 0.4413
case 4: P(a < x <= b) : 0.3213
All of the above code works for me without error. I don't see any differences in the algorithm used to get the values. Not sure if Python generates numbers differently from QBasic, and if that accounts for the reason behind this behavior.
I'm new to both of these languages but QBasic seems very primitive, and it would seem more likely that Python would get the right answer and QBasic the wrong one. But the opposite is happening. It doesn't appear to be something related to any difference in code. On translation, they both seem to be saying the same thing.
I'm interested in the reason why they give the 2 different outcomes. I'm more interested in why Python is giving the wrong answers and QBasic is giving the right ones.
Your Python code is totally wrong. What I think you want it to do is the following:
Take two arrays, a and b, each containing 10000 numbers generated by some random function. (Equivalently, each an array of 10000 samples from data following some given distribution.)
Pair up the values into 10000 pairs, with each pair taking an element from a and an element from b.
Take the sum of each pair.
Count how many of those 10000 pair-sums lie between 0.9 and 1.8
Divide the above count by 10000 to get the probability that any given pair of data drawn from these distributions will sum to between 0.9 and 1.8, and print that probability.
However, your sim(a, b) function is doing something wildly different. Basically, what you're actually doing is:
Concatenate the two 10000 element arrays, forming a 20000-element array of their elements.
Take the sum of any elements in this new 20000-element array that are greater than 0.9.
Divide that sum by 10000 and print it.
This algorithm bears no resemblance to anything in your Q-Basic code.
If I've understood your problem properly, I think what you want your sim function to be is this:
def sim(x_sample, y_sample):
count = 0
for i in range(10000):
if 0.9 <= x_sample[i] + y_sample[i] <= 1.8:
count += 1
probability = count/10000.0
print("P(a < x <= b) : {0:8.4f}".format(probability))
(There are almost certainly more elegant and Pythonic ways to implement the above function, but this way should be easy for a Python newbie to understand.)
Here are the results of tests I ran in the interpreter for your cases 1 to 3, as I've understood them from the QBasic program. I haven't included a version of test 4 because I didn't understand the QBasic code for test 4. The results for the first three tests are what you said they should be, though.
>>> from random import random
>>>
>>> sim([random() for i in range(10000)],
... [random() for i in range(10000)])
P(a < x <= b) : 0.5746
>>>
... from math import log
>>>
>>> sim([-0.5*log(1-random()) for i in range(10000)],
... [-0.5*log(1-random()) for i in range(10000)])
P(a < x <= b) : 0.3405
>>>
... sim([random() for i in range(10000)],
... [2*random() for i in range(10000)])
P(a < x <= b) : 0.4479

How to set minimum value to a division operation in python?

Is there a standard library function which will set a minimum value to a division operation, for example:
min(1, a/b)
This will ensure that min value of operation above will always be 1, never 0.
Such as:
min(1, 1/5)
1
Also, how do I round up a division:
round_up(1/5) = 1
I always get "0" when I divide 1/5, even with ceil function:
math.ceil(1/5)
0
If you want to use floating point division as default, you can do from __future__ import division:
>>> 1/5
0
>>> from __future__ import division
>>> 1/5
0.2
>>> math.ceil(1/5)
1.0
If you need the result to be of integer type, e.g. for indexing, you can use
int(math.ceil(1/5))
The result of 1/5 is an integer already. If you want the floating point version you need to do 1.0/5. The math.ceil function will then work as you expect: math.ceil(1.0/5) = 1.0.
If you're working with variables instead of constants, use the float(x) function to convert an integer into a floating point.
In [4]: 1/5
Out[4]: 0
In [5]: math.ceil(1/5)
Out[5]: 0.0
In [7]: float(1)/5
Out[7]: 0.2
In [8]: math.ceil(float(1)/5)
Out[8]: 1.0
You could make a round up function for integers like this
>>> def round_up(p, q):
... d, r = divmod(p, q)
... if r != 0:
... d += 1
... return d
...
>>> round_up(1, 5)
1
>>> round_up(0, 5)
0
>>> round_up(5, 5)
1
>>> round_up(6, 5)
2
>>>
Your example doesn't work because an integer dividing an integer is an integer.
As for your min question - what you wrote is probably the best you can do.
I don't know about anything in the standard library, but if you are just trying to make sure the answer is never less than 1, the function is pretty easy:
def min_dev(x,y):
ans = x/y
if ans < 1: # ensures answer cannot be 0
return 1
else: # answers greater than 1 are returned normally
return ans
If, instead, you are looking to round up every answer:
def round_up(x,y):
ans = x//y # // is the floor division operator
if x % y == 1: # tests for remainder (returns 0 for no, 1 for yes)
ans += 1 # same as ans = ans + 1
return ans
else:
return ans
This will round up any answer with a remainder.
I believe Python 3.3 (and I know 3.4) return a float by default for integer division: https://docs.python.org/3/tutorial/introduction.html

Categories

Resources