根据条件更改数字中的数字
Changing digits in numbers based on a conditions
在挪威,我们有一种叫做 D 和 S 号码的东西。这些是修改出生日期或月份的国民身份证号码。
D-number
[d+4]dmmyy
S-number
dd[m+5]myy
我有一个包含日期的列,其中一些是正常的 (ddmmyy),一些是格式化为 D 或 S 数字。也缺少前导零。
df = pd.DataFrame({'dates': [241290, #24.12.90
710586, #31.05.86
105299, #10.02.99
56187] #05.11.87
})
dates
0 241290
1 710586
2 105299
3 56187
我编写了这个函数来添加前导零并转换日期,但是这个解决方案感觉不是很好。
def func(s):
s = s.astype(str)
res = []
for index, value in s.items():
# Make sure all dates have 6 digits (add leading zero)
if len(value) == 5:
value = ('0' + value)
# Convert S- and D-dates to regular dates
if int(value[0]) > 3:
# substract 4 from the first digit
res.append(str(int(value[0]) - 4) + value[1:])
elif int(value[2]) > 1:
# subtract 5 from the third digit
res.append(value[:2] + str(int(value[2]) - 5) + value[3:])
else:
res.append(value)
return pd.Series(res)
是否有更流畅、更快速的方法来实现相同的结果?
您可以将 Series 保留为整数,直到最后一步。以下方法的缺点是偏移量与规格不符,可能需要更多的脑力才能理解:
def func2(s):
# In mathematical operations, digits are counted from right
# so "first digit" becomes sixth and "third digit" becomes
# fourth in a 6-digit number
delta = np.select(
[s // 10**5 % 10 > 3, s // 10**3 % 10 > 1],
[4 * 10**5 , 5 * 10**3 ],
0
)
return (s - delta).astype('str').str.pad(6, fillchar='0')
通过用 0 填充来标准化日期,然后分解为 3 列两位数(日、月、年)。应用您的规则并将列合并为 DateTimeIndex
:
# Suggested by @HenryEcker
# Changed: .pad(6, fillchar='0') to .zfill(6)
dates = df['dates'].astype(str).str.zfill(6).str.findall('(\d{2})') \
.apply(pd.Series).astype(int) \
.rename(columns={0: 'day', 1: 'month', 2: 'year'}) \
.agg({'day': lambda d: d if d <= 31 else d - 40,
'month': lambda m: m if m <= 12 else m - 50,
'year': lambda y: 1900 + y})
df['dates2'] = pd.to_datetime(dates)
输出:
>>> df
dates dates2
0 241290 1990-12-24
1 710586 1986-05-31
2 105299 1999-02-10
3 56187 1987-11-05
>>> dates
day month year
0 24 12 1990
1 31 5 1986
2 10 2 1999
3 5 11 1987
在挪威,我们有一种叫做 D 和 S 号码的东西。这些是修改出生日期或月份的国民身份证号码。
D-number
[d+4]dmmyy
S-number
dd[m+5]myy
我有一个包含日期的列,其中一些是正常的 (ddmmyy),一些是格式化为 D 或 S 数字。也缺少前导零。
df = pd.DataFrame({'dates': [241290, #24.12.90
710586, #31.05.86
105299, #10.02.99
56187] #05.11.87
})
dates
0 241290
1 710586
2 105299
3 56187
我编写了这个函数来添加前导零并转换日期,但是这个解决方案感觉不是很好。
def func(s):
s = s.astype(str)
res = []
for index, value in s.items():
# Make sure all dates have 6 digits (add leading zero)
if len(value) == 5:
value = ('0' + value)
# Convert S- and D-dates to regular dates
if int(value[0]) > 3:
# substract 4 from the first digit
res.append(str(int(value[0]) - 4) + value[1:])
elif int(value[2]) > 1:
# subtract 5 from the third digit
res.append(value[:2] + str(int(value[2]) - 5) + value[3:])
else:
res.append(value)
return pd.Series(res)
是否有更流畅、更快速的方法来实现相同的结果?
您可以将 Series 保留为整数,直到最后一步。以下方法的缺点是偏移量与规格不符,可能需要更多的脑力才能理解:
def func2(s):
# In mathematical operations, digits are counted from right
# so "first digit" becomes sixth and "third digit" becomes
# fourth in a 6-digit number
delta = np.select(
[s // 10**5 % 10 > 3, s // 10**3 % 10 > 1],
[4 * 10**5 , 5 * 10**3 ],
0
)
return (s - delta).astype('str').str.pad(6, fillchar='0')
通过用 0 填充来标准化日期,然后分解为 3 列两位数(日、月、年)。应用您的规则并将列合并为 DateTimeIndex
:
# Suggested by @HenryEcker
# Changed: .pad(6, fillchar='0') to .zfill(6)
dates = df['dates'].astype(str).str.zfill(6).str.findall('(\d{2})') \
.apply(pd.Series).astype(int) \
.rename(columns={0: 'day', 1: 'month', 2: 'year'}) \
.agg({'day': lambda d: d if d <= 31 else d - 40,
'month': lambda m: m if m <= 12 else m - 50,
'year': lambda y: 1900 + y})
df['dates2'] = pd.to_datetime(dates)
输出:
>>> df
dates dates2
0 241290 1990-12-24
1 710586 1986-05-31
2 105299 1999-02-10
3 56187 1987-11-05
>>> dates
day month year
0 24 12 1990
1 31 5 1986
2 10 2 1999
3 5 11 1987