如何用Python Pandas清洗含多种混乱值的Year列?
如何用Python Pandas清洗含多种混乱值的Year列?
嘿,刚接触Pandas就碰到这么棘手的年份清洗问题,太懂这种头大的感觉了!尤其是数据集本来就小,丢数据真的肉疼。我给你捋一套实操性强的方法,尽量帮你保住更多数据,而且都是你能上手的Python代码~
首先,咱们不用一开始就想着丢数据,正则确实是解决这类混乱文本的好工具,但不用搞太复杂的,先从提取数字这种基础操作入手就行,5000行的数据量完全不用担心效率问题,Pandas处理起来秒完。
第一步:先理清楚要处理的所有混乱格式
咱们先把你提到的情况归个类,写代码时更有针对性:
- 标准四位年份:比如1998、2001 → 直接用
- 两位数字:比如87 → 转成1987;像10这类小数字就转成2010,这个阈值你可以根据数据集实际年份范围调整
- 年份范围:比如'2011- 2012'、'89 or 90' → 可以取平均、取起始年,甚至拆分成两行数据,把所有信息都保住
- 模糊表述:比如'early 1990's'、'approx 2001' → 提取核心年份,模糊词对应成具体值,比如early 1990's→1990,mid 1990's→1995
第二步:写一个自定义清洗函数
核心思路是写一个能处理单个年份字符串的函数,再用Pandas的apply方法批量整列处理。这个函数用正则提取所有数字,再按不同情况转换:
先导入需要的库:
import pandas as pd import re
然后是带详细注释的清洗函数,每一步都很直白:
def process_single_year(year_str): # 先统一转小写、去掉多余空格,避免大小写/空格干扰判断 year_str = str(year_str).strip().lower() # 处理early/mid/late这类模糊年代词 if 'early' in year_str: # 提取年代(比如early 1990's里的1990) decade = re.findall(r'\d{3}0', year_str) return int(decade[0]) if decade else pd.NA elif 'mid' in year_str: decade = re.findall(r'\d{3}0', year_str) return int(decade[0]) + 5 if decade else pd.NA elif 'late' in year_str: decade = re.findall(r'\d{3}0', year_str) return int(decade[0]) + 9 if decade else pd.NA # 提取字符串里所有连续的数字序列 num_list = re.findall(r'\d+', year_str) if not num_list: # 没提取到数字,返回空值标记 return pd.NA # 情况1:只有一组数字 if len(num_list) == 1: num = num_list[0] if len(num) == 4: # 四位数字直接转整数 return int(num) elif len(num) == 2: # 两位数字,判断是19xx还是20xx num_int = int(num) # 这里的阈值23可以改,比如数据集最晚到2020,就把阈值设为20 return 2000 + num_int if num_int <= 23 else 1900 + num_int else: # 其他长度的数字(比如一位),返回空值 return pd.NA # 情况2:有两组数字(范围类数据) elif len(num_list) == 2: # 把两位数字转成四位完整年份的小工具函数 def to_full_year(n): if len(n) == 4: return int(n) elif len(n) == 2: n_int = int(n) return 2000 + n_int if n_int <=23 else 1900 + n_int else: return pd.NA y1 = to_full_year(num_list[0]) y2 = to_full_year(num_list[1]) if pd.notna(y1) and pd.notna(y2): # 这里可以选:取起始年、结束年、平均,或者返回列表后面拆分数据 # 比如取平均取整 return (y1 + y2) // 2 # 如果想拆分数据,就返回[y1, y2],后面用explode方法 # return [y1, y2] else: return pd.NA # 情况3:超过两组数字(少见),返回空值 else: return pd.NA
第三步:批量清洗Year列
接下来把函数用到你的数据上,记得别直接修改原始列,新生成一个cleaned_year列,方便对比原始数据:
# 假设你的数据集叫df,Year列是原始混乱列 df['cleaned_year'] = df['Year'].apply(process_single_year)
如果刚才你选的是返回列表(拆分范围数据),那再加一步explode:
df['cleaned_year'] = df['Year'].apply(process_single_year) df = df.explode('cleaned_year', ignore_index=True)
这样原来的一行范围数据会变成两行,分别对应两个年份,统计的时候就不会浪费这条数据啦。
第四步:检查清洗结果
清洗完之后,先看看有多少空值,判断能不能接受:
# 统计空值数量和占比 null_count = df['cleaned_year'].isna().sum() print(f"清洗后空值数量:{null_count},占比:{null_count/len(df)*100:.2f}%")
如果空值占比太高(比如超过10%),再回头调整函数,比如加更多规则处理特殊的模糊表述,比如'circa 1995'这类,其实只要提取到1995就行,函数里的re.findall已经能处理了。
给新手的小提醒
- 一定要先备份原始数据!别直接覆盖原列,万一洗坏了还能重来
- 正则不用一开始就搞复杂,
re.findall(r'\d+')就是提取所有连续数字,这是最常用的,先从这个入手 - 5000行数据真的很小,Pandas的
apply完全hold住,不用怕效率问题,先把结果做对再考虑优化
备注:内容来源于stack exchange,提问作者Jubilbee Draws
相关产品推荐
相关产品推荐

