日期型数据从不同系统汇总过来后,字符串形态往往并不一致。有的记录是 2024-05-12 08:30:00,有的写成 2024/11/2 9:15:20,还有的带中文年月日。如果分析只关心月份和日期,就需要把年份和后面的时分秒一并去掉。直接在 Series 上做切片只适合格式完全固定的场景,一旦字段中出现空格、斜杠或中文,固定位置就会失效。更稳妥的做法是先统一解析为标准日期类型,再从日期类型中提取所需信息,这样既能应对格式变化,也能利用 Pandas 的向量化能力提升清洗效率。

一、为什么不能直接对日期字符串做简单截取
很多日期列在数据库里看起来整齐,导出到 CSV 或 Excel 后却会带上不同的时间精度。例如同一个订单时间字段,可能同时出现 2024-05-12 08:30:00 和 2024-05-12,前一个带有完整时分秒,后一个只有日期部分。如果坚持用 str.slice(5, 10) 去截取月份和日期,对于第一种情况能拿到 05-12,对于第二种情况虽然也能拿到 05-12,但前提是年份固定占四位、分隔符固定为短横线。
现实数据往往不会这么理想。只要出现 2024/5/2 这种去零写法,位置切片就会得到 4/5/ 之类的错误结果;如果出现 2024年5月2日,字符串位置和分隔符完全改变,切片逻辑立即失效。因此清洗日期字符串时,不能只盯着当前样本的格式,而要优先考虑能否先把文本解析成真正的日期对象,再按业务需求重新格式化输出。这样即使原始格式发生变化,只要解析阶段能兼容,提取逻辑就不用反复修改。
另外,日期字符串里可能还混着空值、前后空格、时区标记,这些情况都会影响固定位置截取的可靠性。所以第一步不是急着写提取表达式,而是先看一列数据的格式分布,判断是否可以用统一的解析函数处理。
二、用 to_datetime 解析并用 strftime 输出月日
Pandas 提供了 pd.to_datetime,它能把大多数常见日期字符串转换为 datetime64 类型。解析失败时默认会抛出异常,但加上 errors='coerce' 后,失败的位置会被替换为 NaT,这样可以避免因为一个坏值导致整列清洗中断。下面的代码先解析两个常见格式,并查看输出类型。
import pandas as pd
df = pd.DataFrame({
'order_time': ['2024-05-12 08:30:00', '2024/11/2 9:15:20']
})
# 向量化解析,解析失败时用 NaT 代替
df['parsed'] = pd.to_datetime(df['order_time'], errors='coerce')
print(df['parsed'])
解析完成后,可以调用 dt.strftime 按需要输出字符串,例如 %m-%d 表示两位月份、短横线、两位日期。
# 从解析结果中提取月-日字符串
df['month_day'] = df['parsed'].dt.strftime('%m-%d')
print(df[['order_time', 'month_day']])
strftime 返回的是字符串,不会保留日期类型。如果后续还要基于月份和日期做分组统计,字符串类型完全可以满足;但如果后续需要按日期排序或计算时间差,仅保留月日字符串就不够。此时可以改用 dt.month 和 dt.day 两个整数属性拼接,既能补零,也方便把数值单独抽出来做条件判断。
# 也可以用 month 和 day 属性拼接,适合需要补零或单独使用数值的场景
df['month_day_attr'] = (
df['parsed'].dt.month.astype(str).str.zfill(2)
+ '-'
+ df['parsed'].dt.day.astype(str).str.zfill(2)
)
print(df[['order_time', 'month_day_attr']])
如果日期字符串包含中文“年、月、日”或者英文月份缩写,pd.to_datetime 的自动推断可能解析失败。此时可以先用 format 参数指定模板,或者先做一轮文本替换,把中文单位改成短横线再解析。对于混合格式,Pandas 2.0 及以上版本支持 format='mixed',但对中文日期的兼容仍然有限,需要结合实际数据测试。
三、字符串切片与正则提取的适用边界
如果数据来源非常稳定,格式永远是 YYYY-MM-DD HH:MM:SS,那么使用 .str.slice(5, 10) 是最快的方法之一。因为它不需要解析日期,只做字符串截取,几乎没有类型转换开销。
# 仅当格式严格为 YYYY-MM-DD HH:MM:SS 时,位置切片才可靠 df['slice_md'] = df['order_time'].str.slice(5, 10) print(df[['order_time', 'slice_md']])
但这种方法非常脆弱。只要字段里有一个值写成 2024-5-2 8:30:00,位置 5 到 10 的内容就不再是月份和日期,而可能是 5-2 8 之类的片段。更隐蔽的问题是,有些日期字符串前面带有空格或不可见字符,索引位置整体偏移,切片结果会完全错误,而且不易被肉眼发现。因此固定切片只适合已经确认格式绝对统一的列,通常作为临时处理手段,不建议作为标准清洗流程。
正则表达式比固定切片灵活一些,可以同时匹配短横线、斜杠和中文日期中的月日部分。下面实现一个简单的提取函数,优先匹配四位年份后面的月份和日期,再返回补零后的字符串。
import re
def extract_month_day(text):
if pd.isna(text):
return None
# 优先匹配四位年份后面的月-日
match = re.search(r'\d{4}[-/年](\d{1,2})[-/月](\d{1,2})', str(text))
if match:
return f"{int(match.group(1)):02d}-{int(match.group(2)):02d}"
return None
df['regex_md'] = df['order_time'].apply(extract_month_day)
print(df[['order_time', 'regex_md']])
正则的优势在于可以处理带中文前后缀的文本,比如“下单时间:2024年5月2日 08:30:00”也能提取出 05-02。但它的缺点也很明显:可维护性差,遇到“20240512”“12 May 2024”等新格式就要继续补规则;而且 apply 逐行执行正则在大数据量下性能较差。因此正则适合在格式复杂但数据量不大,或者只需要一次性清理的场景中使用。
四、混合格式、空值和时区的加固处理
真实数据往往是一列混合格式,同时包含 YYYY-MM-DD、MM/DD/YYYY、中文日期、空字符串和 None。清洗前应先统一解析,并统计 NaT 的数量,判断有多少值没有匹配到日期格式。下面的示例使用 format='mixed' 处理英文数字混合格式,同时观察中文日期和空值的解析结果。
mixed = pd.Series([
'2024-05-12 08:30:00',
'2024/11/2 9:15:20',
'2024年1月3日 22:10:05',
'12/31/2024 23:59:59',
None,
''
])
# format='mixed' 允许列内同时存在多种日期格式,但中文日期需额外处理
dt_mixed = pd.to_datetime(mixed, errors='coerce', format='mixed')
print(dt_mixed)
解析后得到 NaT 的位置,既不能直接调用字符串方法,也不能用于排序。通常可以先用 isna 统计坏值比例,再决定是删除、填充还是单独划分出来走正则补提。如果业务允许,填充成固定值如“未知”或空字符串也能让后续流程不中断。关键是要显式处理这些缺失,而不是让它们悄悄混入结果。
时区也是容易忽略的问题。当日期字符串为 2024-05-12T08:30:00Z 或 2024-05-12 08:30:00+08:00 时,pd.to_datetime 能识别时区,但同一时刻在不同时区对应的自然日期可能不同。例如 UTC 时间凌晨 1 点在北京就是上午 9 点,日期没有变化,但反过来就可能跨天。若清洗目标只是展示用月日,需要先明确按哪个时区解释字符串,避免时区转换造成日期偏移。可以在解析时指定 utc=True 或用 dt.tz_convert 转换到业务时区后再提取月日。
五、保留字符串还是日期类型:按用途选择
用 strftime 或字符串拼接得到月日后,字段类型就变成了 object 或 string。这种形态适合直接展示、拼接报表或作为分组标签,但在排序时会按字典序排列。例如 10-01 会排在 02-01 前面,因为字符串比较首先看第一位。如果业务上需要按月日做时间序列排序,就必须保留日期类型或至少使用可排序的数值格式,比如把月日转成 MM-DD 的两位字符串可以缓解,但跨年份排序仍然需要年份参与。
# 如果想保留完整日期但丢弃时分秒,用 normalize df['date_only'] = pd.to_datetime(df['order_time'], errors='coerce').dt.normalize() print(df[['order_time', 'date_only']])
dt.normalize() 会把时间部分归零,但年份仍然存在。也就是说,如果你要的只是月和日,又希望字段继续作为日期类型参与比较和计算,实际上没有完美的纯“月日”日期类型,因为标准日期类型必须包含年份。此时通常的做法是保留完整日期类型用于计算,同时额外生成一个月日字符串列用于展示。这样计算层和展示层各司其职,既不会丢失信息,也能满足下游需求。
性能方面,固定切片最快,向量化 to_datetime 次之,逐行 apply 加正则最慢。对于百万行以上的数据,建议优先使用向量化解析和 dt 访问器,只在确实需要复杂规则时才引入正则。清洗完成后最好检查一遍唯一值分布,确认提取出的月日没有异常值,比如 13-45 这类不可能出现的组合,这往往是解析逻辑没有覆盖到的坏样本。
总结来说,从日期字符串中移除年份后的多余信息,核心是先识别列内格式分布,再选择解析方式。固定格式用切片最省事,混合格式用 to_datetime 加 strftime 最稳,带中文或非标准格式再用正则补提。无论用哪种方式,都要显式处理空值和坏值,并根据后续分析对类型的要求决定返回字符串还是保留日期类型。
Pandas数据清洗日期字符串处理数据预处理修改时间:2026-10-06 16:51:33