Идея состоит в том, чтобы создать вспомогательный словарь с возможными форматами даты-времени с количеством букв для нарезки и преобразования списка - errors='coerce'
создать NaT
с для несопоставленных значений:
from functools import reduce
#add zeros to length 6
s = df.inv_id.str.replace(r'\D+', '').str.zfill(6)
formats = {'%m%d%y':6,
'%y%m%d':6,
'%Y%m%d':8,
'%m%d%Y':8}
L = [pd.to_datetime(s.str[:v], format=k, errors='coerce') for k,v in formats.items()]
Но некоторые форматы должны конвертировать плохо, поэтому эти даты за пределами диапазона конвертируются в NaT
:
L = [x.where(x.between('2000-01-01', pd.datetime.now())) for x in L]
И объединить все значения, отличные от NaT, на Series.combine_first
:
s2 = reduce(lambda l,r: pd.Series.combine_first(l,r), L)
print (s2)
0 2018-07-12
1 2018-07-12
2 2018-05-01
3 2018-05-01
4 2018-04-25
5 2018-04-25
6 2018-06-15
7 2018-06-15
8 2018-01-13
9 2018-01-13
10 2017-12-20
11 2017-12-20
12 NaT
13 NaT
Name: inv_id, dtype: datetime64[ns]
Последняя проверка +-180
дней:
df['new'] = s2.between(s2 - pd.Timedelta(180, unit='d'), s2 + pd.Timedelta(180, unit='d'))
print (df)
cluster_id amount inv_id inv_date new
0 1 309.9 07121830990 2018-07-12 True
1 1 309.9 07121830990 2018-07-12 True
2 2 3130.0 20180501313000B 2018-05-01 True
3 2 3130.0 20180501313000B 2018-05-01 True
4 3 3330.5 201804253330.50 2018-04-25 True
5 3 3330.5 201804253330.50 2018-04-25 True
6 4 70.0 61518 2018-06-15 True
7 4 70.0 61518 2018-06-15 True
8 5 100.0 011318 2018-01-13 True
9 5 100.0 011318 2018-01-13 True
10 6 50.0 12202017 2017-12-20 True
11 6 50.0 12202017 2017-12-20 True
12 7 101.0 0000014482 2017-10-01 False
13 7 101.0 0000014482 2017-10-01 False
EDIT:
Добавлено решение для удаления подстрок с концов:
import re
from functools import reduce
df['amt_str'] = (df['amount']*100).round().astype(int).astype(str)
df['inv_str'] = df.inv_id.str.replace(r'\D+', '').str.zfill(6)
#https://stackoverflow.com/a/1038845/2901002
df['inv_str'] = df.apply(lambda x: re.sub('{}$'.format(x['amt_str']),'', x['inv_str']),axis=1)
print (df)
cluster_id amount inv_id inv_date amt_str inv_str
0 1 309.9 07121830990 2018-07-12 30990 071218
1 1 309.9 07121830990 2018-07-12 30990 071218
2 2 3130.0 20180501313000B 2018-05-01 313000 20180501
3 2 3130.0 20180501313000B 2018-05-01 313000 20180501
4 3 3330.5 201804253330.50 2018-04-25 333050 20180425
5 3 3330.5 201804253330.50 2018-04-25 333050 20180425
6 4 70.0 61518 2018-06-15 7000 061518
7 4 70.0 61518 2018-06-15 7000 061518
8 5 100.0 011318 2018-01-13 10000 011318
9 5 100.0 011318 2018-01-13 10000 011318
10 6 50.0 12202017 2017-12-20 5000 12202017
11 6 50.0 12202017 2017-12-20 5000 12202017
12 7 101.0 0000014482 2017-10-01 10100 0000014482
13 7 101.0 0000014482 2017-10-01 10100 0000014482
formats = {'%m%d%y':6,
'%y%m%d':6,
'%Y%m%d':8,
'%m%d%Y':8}
L=[pd.to_datetime(df['inv_str'].str[:v],format=k, errors='coerce') for k,v in formats.items()]
L = [x.where(x.between('2000-01-01', pd.datetime.now())) for x in L]
s2 = reduce(lambda l,r: pd.Series.combine_first(l,r), L)
df['new'] = s2.between(s2 - pd.Timedelta(180, unit='d'), s2 + pd.Timedelta(180, unit='d'))
print (df)
cluster_id amount inv_id inv_date amt_str inv_str new
0 1 309.9 07121830990 2018-07-12 30990 071218 True
1 1 309.9 07121830990 2018-07-12 30990 071218 True
2 2 3130.0 20180501313000B 2018-05-01 313000 20180501 True
3 2 3130.0 20180501313000B 2018-05-01 313000 20180501 True
4 3 3330.5 201804253330.50 2018-04-25 333050 20180425 True
5 3 3330.5 201804253330.50 2018-04-25 333050 20180425 True
6 4 70.0 61518 2018-06-15 7000 061518 True
7 4 70.0 61518 2018-06-15 7000 061518 True
8 5 100.0 011318 2018-01-13 10000 011318 True
9 5 100.0 011318 2018-01-13 10000 011318 True
10 6 50.0 12202017 2017-12-20 5000 12202017 True
11 6 50.0 12202017 2017-12-20 5000 12202017 True
12 7 101.0 0000014482 2017-10-01 10100 0000014482 False
13 7 101.0 0000014482 2017-10-01 10100 0000014482 False