正規表現での小数部削除とDecimal.quantize()による四捨五入

00:03:32.5のように、秒に小数部分が含まれている場合、秒からその小数部分(小数点以下の数字)だけを削除、もしくは、四捨五入する方法を紹介したいと思います。秒が小数表現になっていると、datetime.strptimeを使って日時のフォーマットを変換できないので、どうしても小数部分を削除する作業が必要になってきます。

スポンサーリンク

strptimeは小数は変換してくれない

import datetime

s = '2019/1/1 00:03:32.5'

a = datetime.datetime.strptime(s, '%Y/%m/%d %H:%M:%S')

print(type(a))
print(a)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-62-89f603be818b> in <module>
      3 s = '2019/1/1 00:03:32.5'
      4 
----> 5 a = datetime.datetime.strptime(s, '%Y/%m/%d %H:%M:%S')
      6 
      7 print(type(a))

~/.pyenv/versions/miniconda3-latest/envs/py373/lib/python3.7/_strptime.py in _strptime_datetime(cls, data_string, format)
    575     """Return a class cls instance based on the input string and the
    576     format string."""
--> 577     tt, fraction, gmtoff_fraction = _strptime(data_string, format)
    578     tzname, gmtoff = tt[-2:]
    579     args = tt[:6] + (fraction,)

~/.pyenv/versions/miniconda3-latest/envs/py373/lib/python3.7/_strptime.py in _strptime(data_string, format)
    360     if len(data_string) != found.end():
    361         raise ValueError("unconverted data remains: %s" %
--> 362                           data_string[found.end():])
    363 
    364     iso_year = year = None

ValueError: unconverted data remains: .5

32.5秒の.5が余分なために変換ができません。このエラーを解消するには小数部分を削除するしかありません。

スポンサーリンク

正規表現による小数部位の削除

import re

s = '2019/1/1 00:03:32.5'
s = re.sub('\.\d{1,}','',s)
a = datetime.datetime.strptime(s, '%Y/%m/%d %H:%M:%S')

print(type(a))
print(a)
<class 'datetime.datetime'>
2019-01-01 00:03:32

ただ、上記の方法だと、小数部分が四捨五入されないので、若干問題はあるのですが、秒が0.1秒〜0.9秒違っても大して問題にならないと思われるので、これで良しとしました。しかしながら、どうしても、四捨五入しないと困るという人もいるので、そんな人のために、以下に四捨五入をしてくれるコードを書いておきます。

s = '2019/1/1 00:03:32.5'
s1 = re.search('\d{2}\.\d{1,}',s)
s2 = float(s1.group())
s3 = round(s2)
s3
32

python3のroundは、0.5を1に四捨五入してくれません。なので、roundを使うことはできません。代わりに、decimal quantizeのROUND_HALF_UPメソッドを使って四捨五入します。

スポンサーリンク

Decimal.quantize()による四捨五入

from decimal import Decimal, ROUND_HALF_UP

s = '2019/1/1 00:03:32.5'
s1 = re.search('\d{2}\.\d{1,}',s)
s2 = Decimal(s1.group()).to_integral_value(rounding=ROUND_HALF_UP)
s3 = re.sub('\d{2}\.\d{1,}',str(s2),s)
s3
'2019/1/1 00:03:33'

Decimal.quantize()が、32.5を33に四捨五入してくれました。

s = '2019/1/1 00:03:32.5'
s1 = re.search('\d{2}\.\d{1,}',s)
s2 = Decimal(s1.group()).to_integral_value(rounding=ROUND_HALF_UP)
s3 = re.sub('\d{2}\.\d{1,}',str(s2),s)

a = datetime.datetime.strptime(s3, '%Y/%m/%d %H:%M:%S')

print(type(a))
print(a)
<class 'datetime.datetime'>
2019-01-01 00:03:33

roundは0.5をゼロに四捨五入してしまいます。

round(0.5)
0

しかし、0.51は1に四捨五入します。

round(0.51)
1

ですので、どうしても0.5を1に四捨五入してくれなければ困ると言う人以外は、従来通り、roundを使えば良いかと思います。


スポンサーリンク
スポンサーリンク