当前位置: 首页 > article >正文

BOSS直聘招聘数据分析的第一步:用Python进行深度清洗

BOSS直聘招聘数据分析的第一步:用Python进行深度清洗

在数据分析和数据挖掘过程中,数据清洗是确保分析结果准确性的重要步骤。本文以从 BOSS直聘 爬取的“测试工程师”职位数据为例(数据爬取详见上篇文章),介绍如何使用Python对爬取的数据进行清洗,确保数据质量,以便后续分析和建模。


数据清洗的目标与方法

在数据分析前,清洗数据的主要目标是确保数据的一致性和完整性。常见的清洗任务包括:

  1. 处理缺失值:填充或删除缺失数据。
  2. 删除重复数据:去除重复行,确保数据唯一性。
  3. 标准化字段格式:统一字段值的格式(如薪资范围、年限要求等)。
  4. 检测并处理异常值:去除不合理的数据点,减少对分析结果的干扰。
  5. 清洗文本数据:清理职位描述等文本字段,为后续的文本分析做好准备。

数据清洗的代码实现

在数据清洗中,我们按照以下思路进行逐步实现,并为每个步骤提供代码示例和详细说明。


1. 数据读取与缺失值处理
1.1 数据读取

数据存储在 Excel 文件中,我们使用 pandas.read_excel() 读取数据。

import pandas as pd

# 读取Excel数据
df = pd.read_excel('your_data.xlsx')  # 替换为实际文件路径
1.2 检查缺失值

通过 isnull().sum() 检查每列的缺失值数量,了解缺失数据的分布情况。

# 检查缺失值分布
print("缺失值情况:")
print(df.isnull().sum())
1.3 填充或删除缺失值

根据字段的重要性和缺失值的性质,选择填充或删除策略。例如:

  • 薪资描述公司名称 等字段:填充为空字符串 ""
  • 年限要求学历要求 等字段:填充为“未指定”或其他默认值。
  • 缺失较多的数据行可以删除。
# 填充缺失值
df['薪资描述'].fillna("", inplace=True)
df['公司名称'].fillna("", inplace=True)
df['年限要求'].fillna("未指定", inplace=True)
df['学历要求'].fillna("未指定", inplace=True)

# 删除超过50%字段缺失的行
df.dropna(thresh=len(df.columns)*0.5, axis=0, inplace=True)

2. 重复数据处理

通过 duplicated() 检查数据是否存在重复行,删除多余数据以保证数据唯一性。

# 检查重复数据
duplicates = df.duplicated().sum()
print(f"重复行数量:{duplicates}")

# 删除重复行
df.drop_duplicates(inplace=True)

3. 数据格式标准化

在招聘数据中,字段值的格式往往不一致。以下代码标准化了薪资、年限和学历字段。

3.1 薪资描述的标准化

将薪资字段(如 “10k-15k” 或 “年薪15万元”)统一为数字表示,并以区间中位数表示。

import re

def standardize_salary(salary):
    """标准化薪资描述为数值"""
    if isinstance(salary, str):
        salary = salary.strip()
        # 匹配薪资范围(如 "10k-15k")
        match = re.match(r'(\d+)(k|万)?-(\d+)(k|万)?', salary)
        if match:
            min_salary = int(match.group(1))
            max_salary = int(match.group(3))
            unit = 1000 if match.group(2) in ['k', 'K'] else 10000
            return (min_salary + max_salary) * unit / 2
        # 匹配单一薪资(如 "年薪15万")
        match_annual = re.search(r'年薪(\d+)万', salary)
        if match_annual:
            return int(match_annual.group(1)) * 10000
    return None  # 返回None表示无法识别的薪资格式

# 应用薪资标准化函数
df['薪资描述'] = df['薪资描述'].apply(standardize_salary)
3.2 年限要求的标准化

统一年限格式(如 “1-3年” 转为平均年限,“3年以上” 转为单一值)。

def standardize_experience(experience):
    """标准化年限要求为数值"""
    if isinstance(experience, str):
        experience = experience.strip()
        # 匹配年限范围(如 "1-3年")
        match = re.match(r'(\d+)-(\d+)年', experience)
        if match:
            return (int(match.group(1)) + int(match.group(2))) / 2
        # 匹配单一年限(如 "3年以上")
        elif '年以上' in experience:
            return int(experience.split('年')[0])
        # 无经验或不限
        elif '不限' in experience or '应届生' in experience:
            return 0
    return None

# 应用年限标准化函数
df['年限要求'] = df['年限要求'].apply(standardize_experience)
3.3 学历要求的标准化

将学历要求字段统一为规范格式,如 “本科”、“硕士” 等。

def standardize_education(education):
    """标准化学历要求"""
    if isinstance(education, str):
        if '本科' in education:
            return '本科'
        elif '硕士' in education:
            return '硕士'
        elif '博士' in education:
            return '博士'
        elif '大专' in education:
            return '大专'
    return '不限'

# 应用学历标准化函数
df['学历要求'] = df['学历要求'].apply(standardize_education)

4. 异常值检测

对数值字段(如薪资和年限)使用箱线图进行异常值检测,并删除不合理数据。

import matplotlib.pyplot as plt

# 绘制箱线图
plt.figure(figsize=(10, 6))
df['薪资描述'].plot(kind='box')
plt.title("薪资分布箱线图")
plt.show()

# 删除异常值(薪资低于3000或高于30000的职位)
df = df[(df['薪资描述'] >= 3000) & (df['薪资描述'] <= 30000)]

5. 文本数据清洗

对岗位描述等文本字段,去除符号和空格,并进行分词处理。

import jieba

def clean_text(text):
    """清洗文本数据"""
    text = re.sub(r'[^\w\s]', '', text)  # 去除符号
    return " ".join(jieba.cut(text))  # 分词处理

# 应用文本清洗函数
df['岗位要求'] = df['岗位要求'].apply(clean_text)

6. 结果检查与保存

清洗完成后,可以检查清洗后的数据并保存为新的 Excel 文件。

# 查看清洗后的数据
print("清洗后的数据:")
print(df.head())

# 保存清洗后的数据
df.to_excel('cleaned_job_data.xlsx', index=False)

完整实现代码示例

import pandas as pd
import re
import numpy as np


def standardize_salary(salary):
    """标准化薪资数据,转换为数值形式"""
    if isinstance(salary, str):
        salary = salary.strip()

        # 匹配薪资范围,如"6-9K"
        match = re.match(r'(\d+)-(\d+)(K|k|万)?', salary)
        if match:
            min_salary = int(match.group(1)) * (1000 if match.group(3).lower() == 'k' else 10000)
            max_salary = int(match.group(2)) * (1000 if match.group(3).lower() == 'k' else 10000)
            return (min_salary + max_salary) / 2  # 取区间的平均值

        # 匹配年薪格式,如"年薪15万·14薪"
        match_annual = re.match(r'(\d+)-(\d+)(K|k)?[\s·]*(\d+)薪', salary)
        if match_annual:
            base_salary = int(match_annual.group(1)) * (1000 if match_annual.group(3).lower() == 'k' else 10000)
            extra_salary = int(match_annual.group(2)) * (1000 if match_annual.group(3).lower() == 'k' else 10000)
            extra_months = int(match_annual.group(4))
            return (base_salary + extra_salary) * extra_months / 12  # 按年薪计算

        # 处理其他情况,如"10K"或"15万"
        match_single = re.match(r'(\d+)(K|k|万)?', salary)
        if match_single:
            return int(match_single.group(1)) * (1000 if match_single.group(2).lower() == 'k' else 10000)

        # 其他无效的薪资格式返回NaN
        return np.nan
    return np.nan


def standardize_experience(experience):
    """标准化年限数据,转换为数值"""
    if isinstance(experience, str):
        experience = experience.strip()

        # 匹配年限范围,如“1-3年”
        match = re.match(r'(\d+)-(\d+)年', experience)
        if match:
            return (int(match.group(1)) + int(match.group(2))) / 2  # 取年限区间的中位数

        # 匹配“经验不限”
        if '经验不限' in experience:
            return 0  # 经验不限时认为是0年

        # 匹配“应届生”
        if '应届生' in experience:
            return 0  # 应届生视为0年经验

        # 匹配“大于5年”等描述
        match_greater_than = re.match(r'(\d+)年.*以上', experience)
        if match_greater_than:
            return int(match_greater_than.group(1))  # 返回最小年限

        # 无法匹配的返回NaN
        return np.nan
    return np.nan


def standardize_education(education):
    """标准化学历要求"""
    if isinstance(education, str):
        education = education.strip().lower()

        # 处理本科以上,本科及其他
        if '本科' in education:
            return '本科'
        elif '硕士' in education:
            return '硕士'
        elif '博士' in education:
            return '博士'
        elif '大专' in education:
            return '大专'
        else:
            return '不限'
    return '不限'


def standardize_company_size(size):
    """标准化公司规模,清理异常值"""
    if isinstance(size, str):
        size = size.strip()

        # 匹配数字
        match = re.match(r'(\d+)', size)
        if match:
            size_num = int(match.group(1))
            if size_num < 10 or size_num > 100000:
                return np.nan  # 异常的公司规模
            return size_num
    return np.nan


def clean_job_data(df):
    """对招聘数据进行清洗"""

    # 薪资描述清洗
    df['薪资描述'] = df['薪资描述'].apply(standardize_salary)
    df = df[(df['薪资描述'] >= 3000) & (df['薪资描述'] <= 30000)]  # 薪资限制

    # 年限要求清洗
    df['年限要求'] = df['年限要求'].apply(standardize_experience)
    df = df[(df['年限要求'] >= 0) & (df['年限要求'] <= 40)]  # 年限限制

    # 学历要求清洗
    df['学历要求'] = df['学历要求'].apply(standardize_education)

    # 公司规模清洗
    df['公司规模'] = df['公司规模'].apply(standardize_company_size)

    # 返回清洗后的数据
    return df


# 使用示例
if __name__ == "__main__":
    df = pd.read_excel('jobs.xlsx')  # 假设你已经爬取的数据存储在此文件中
    cleaned_df = clean_job_data(df)
    cleaned_df.to_excel('cleaned_job_data.xlsx', index=False)  # 保存清洗后的数据

清洗后的数据
清洗后的数据

总结

上述代码实现了以下功能:

  1. 缺失值处理:对缺失值进行了合理的填充或删除。
  2. 重复数据处理:删除了重复的行。
  3. 数据格式标准化:薪资、年限、学历等字段进行了标准化。
  4. 异常值检测:通过箱线图检测并删除了异常的薪资数据。
  5. 文本数据清洗:对岗位描述、任职要求进行了文本清洗和分词处理。

清洗后的数据更加规范和一致,为后续的数据分析和建模打下了坚实基础。完整代码已附上,欢迎试用并根据需求进行优化!


http://www.kler.cn/a/463192.html

相关文章:

  • Kafka配置公网或NLB访问(TCP代理)
  • 聆听音乐 1.5.9 | 畅听全网音乐,支持无损音质下载
  • 服务器数据恢复—服务器硬盘亮黄灯的数据恢复案例
  • mysql 死锁案例及简略分析
  • docker 安装influxdb
  • 信息科技伦理与道德1:绪论
  • 中科汉玉-舆情感知,品牌声誉管理,政企舆情大数据服务平台
  • Django Settings 优化与常用配置指南
  • 安卓入门一 Java基础
  • 7.傅里叶级数练习题
  • ARM公司
  • 二叉树的实现
  • Redis 5设计与源码分析读书笔记
  • 刷机TP TP-Link-WDR5660【持续更新】
  • 常用的公共 NTP(网络时间协议)服务器
  • Java 开发中的指定外部 Jar 路径详解
  • ajax是什么?作用是什么?交互流程有哪些阶段?
  • SQL-leetcode-182. 查找重复的电子邮箱
  • 深入探索openEuler Kernel:操作系统的心脏
  • 解释dash中的layout = go.Layout( yaxis={domain: [0, 0.50]}, yaxis2={domain: [0.51
  • 计算机网络期末复习之数据链路层
  • WebRTC的线程模型
  • SpringBoot 集成mybatis-plus
  • Vue.js组件开发-实现无感刷新Token
  • Spring web 琐碎知识点 配置文件 会话技术
  • 2.flask中使用装饰器统一验证用户登录