3

My input looks like below:

Name Sep     Oct
Amy  833.33  833.33
Eve  20774.5  0

My Expected Output is:

Name Sep     Oct
Amy  833     833
Eve  20775   0

When I apply np.ceil to round 0.5 to nearest integer, my output becomes:

Name Sep     Oct
Amy  834     834
Eve  20775   0

How to apply np.ceil only to values having decimal greater than or equal to 0.5? Or is there any other way to get my desired output.

Anurag Dabas
  • 23,866
  • 9
  • 21
  • 41

4 Answers4

2

You could use the np.round and cast to int .

like this:

int(np.round(833.33))

Output:

833

Or b
  • 666
  • 1
  • 5
  • 22
1

np.ceil() always rounds up while np.floor() always rounds down.

np.round() is close but does not always round up for *.5. As @Mark Ransom suggested, it turns out this rounding inconsistency is by design (emphasis added):

numpy.around(a, decimals=0, out=None)

Evenly round to the given number of decimals.

So for example 20774.5 rounds down to 20774 while 20775.5 rounds up to 20776.

There are some workarounds in this SO post. Here's one way to applymap() one of those workarounds:

df[['Sep', 'Oct']] = df[['Sep', 'Oct']].applymap(
    lambda x: np.ceil(x) if float(x) % 1 >= 0.5 else np.round(x))

#   Name      Sep    Oct
# 0  Amy    833.0  833.0
# 1  Eve  20775.0    0.0
tdy
  • 36,675
  • 19
  • 86
  • 83
  • I have tried the above, but my number remains at 20774.0 instead of 20775 – Akshaya shiva Mar 27 '21 at 14:01
  • 1
    I think that's just one of the occasional artifacts of floating point precision. See: [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – tdy Mar 27 '21 at 14:04
  • 1
    @tdy it could also be round to even mode. It's hard to tell without printing the number to at least 17 digits. – Mark Ransom Apr 29 '21 at 20:59
  • @MarkRansom good point, that's indeed the case. updated to reflect this – tdy Apr 29 '21 at 21:25
  • 1
    @Akshayashiva it turns out this rounding inconsistency is actually due to "banker rounding" and is by design (not just a floating point artifact). i updated the answer to show a workaround for restoring "school rounding" – tdy Apr 29 '21 at 21:28
0

I fixed my output by applying the following query:

df['Sep'][(df['Sep']%1) >= 0.5] = df['Sep'].apply(np.ceil)
0

inpt_strg = '''

Name Sep Oct
Amy 833.33 833.33
Eve 20774.5 0

'''
s_lst = inpt_strg.splitlines()
n_lst = [list(str.split(" ")) for str in s_lst]

def fl(i):
 try: return float (i)
 except ValueError: return i

def rd(i):
  if isinstance(i, float):
    if (abs(i)%1)*10>=5:return int (i) + 1
    else: return int (i)
  else: return i

r_lst = [[rd(fl(i)) for i in sub] for sub in n_lst]

from tabulate import tabulate
print(tabulate(r_lst,tablefmt='plain'))

Output:-

Name  Sep    Oct
Amy   833    833
Eve   20775  0
kevolegend
  • 11
  • 4