1. 为什么数据挖掘值得你花时间搞明白数据挖掘这个词听起来挺唬人的很多刚入门的朋友一看到就联想到机器学习、深度学习、神经网络那一堆东西觉得门槛高得不行。但我干了这么多年可以很负责任地告诉你数据挖掘的核心不在于算法有多复杂而在于你能不能把数据看懂、把问题定义清楚。工具和代码只是手段思维才是关键。我写这个系列就是想用Python这门语言把数据挖掘从“听起来很厉害”变成“你也能上手跑起来”的东西。第一篇咱们不碰模型不调参就干一件事认识数据。你可能会问认识数据有什么好讲的打开文件看一眼不就行了还真不是。我见过太多项目模型跑出来效果差回头一查数据本身就有问题——缺失值没处理、异常值没发现、字段含义理解错了。这些问题如果不在第一步解决后面全是白费功夫。这篇文章适合谁看如果你有一点Python基础知道列表和字典是什么能写个for循环那就够了。如果你连Python都没装也没关系我会把环境配置的步骤写清楚。如果你已经做过一些数据分析的工作但总觉得不够系统这篇文章也能帮你把思路理一理。总之不管你是学生、转行的朋友还是工作中需要跟数据打交道的职场人只要你想用Python把数据挖掘这件事搞明白这篇内容就是为你准备的。咱们这个系列的整体规划是这样的先从认识数据开始然后讲数据预处理、探索性分析、特征工程再往后才是建模和评估。每一步我都会用真实的案例数据来演示代码可以直接复制运行。我不喜欢那种只讲理论不给代码的教程也不喜欢只贴代码不解释原理的文章。咱们的目标是知其然也知其所以然。2. 数据挖掘到底在挖什么2.1 从一个生活场景理解数据挖掘的本质咱们先不讲定义讲个场景。假设你是一个超市的店长手里有过去一年的销售小票数据。每张小票上记录了什么时间、哪个收银台、卖了哪些商品、每个商品的数量和单价、总金额、有没有用优惠券。这些数据每天都在产生堆在系统里看起来就是一堆流水账。现在问题来了你想知道哪些商品经常被一起买走好把它们摆在一起促进销量你想知道哪些顾客可能快要流失了好提前发优惠券挽留你想知道下周该进多少货避免积压或者断货。这些问题靠人眼去看小票是看不出来的但数据挖掘可以帮你从这堆流水账里找出规律。所以数据挖掘的本质是什么就是从大量看似杂乱的数据中提取出有价值的、之前不知道的、可操作的信息。注意这三个关键词有价值、之前不知道、可操作。如果一条信息你本来就知道或者知道了也没法指导行动那它就不算数据挖掘的成果。再往深一层说数据挖掘做的事情可以归为几大类分类这个东西属于哪一类、回归这个数值大概是多少、聚类哪些东西比较相似、关联规则哪些东西经常一起出现、异常检测哪些东西不太对劲。咱们后面会一个一个展开但你现在只需要记住数据挖掘不是目的解决问题才是。2.2 数据挖掘和数据分析、机器学习的边界在哪很多朋友分不清数据挖掘、数据分析、机器学习这几个概念我用自己的理解给你捋一捋。数据分析更偏向于描述和解释已经发生的事情。比如你算一下上个月的销售额环比增长了多少哪个地区的业绩最好这是数据分析。它的重点是“发生了什么”和“为什么发生”。数据挖掘更偏向于从数据中发现模式和规律这些规律可能是你事先没想到的。比如你发现买尿布的人经常也会买啤酒这就是一个模式。它的重点是“还有什么是我不知道的”。机器学习则是实现数据挖掘的一种技术手段。你可以用机器学习算法来做分类、聚类、预测也可以用统计方法来做。机器学习关注的是如何让计算机从数据中学习规律而数据挖掘关注的是从数据中挖出什么有价值的规律。两者有交集但侧重点不同。在实际工作中这三者是混在一起用的。你不可能只做数据分析不做数据挖掘也不可能只谈机器学习不管业务问题。所以我的建议是不要纠结概念先把工具用起来把问题解决掉。2.3 Python凭什么成为数据挖掘的首选工具这个问题我被问过很多次。R语言在统计领域很强SQL在数据库查询方面无可替代Excel在小型数据集上也很方便。但Python的优势在于生态完整、上手快、社区活跃、能打通从数据采集到模型部署的全流程。具体来说Python做数据挖掘主要靠这几个库NumPy处理数值计算的基础库数组操作比原生列表快很多Pandas处理表格数据的利器读取CSV、Excel、SQL都不在话下Matplotlib和Seaborn画图用的帮你把数据可视化Scikit-learn机器学习算法的集合分类、回归、聚类都有SciPy科学计算相关的高级功能这些库都是开源的文档齐全遇到问题网上搜一下基本都能找到答案。而且Python的语法比较接近自然语言读起来不费劲。我见过很多非计算机专业的朋友学了两周Python就能自己写脚本处理数据了这在其他语言里是很难想象的。当然Python也不是没有缺点。它的运行速度比C、Java慢处理超大规模数据时可能会吃力。但对于绝大多数数据挖掘任务来说这个速度已经够用了。真到了性能瓶颈也有Spark、Dask这些分布式计算框架可以配合使用。3. 动手之前先把环境搭好3.1 安装Python和包管理工具如果你还没装Python我建议直接装Anaconda。Anaconda是一个Python发行版自带了很多数据科学相关的库省得你一个个去装。下载地址你搜一下“Anaconda官网”就能找到选对应操作系统的版本一路下一步就行。装好之后你会得到一个叫conda的命令行工具。conda既可以管理Python版本也可以管理各种库。我习惯给每个项目建一个独立的环境这样不同项目之间的库版本不会打架。建环境的命令是conda create -n datamining python3.10这行命令的意思是创建一个叫datamining的环境里面装Python 3.10。创建好之后激活这个环境conda activate datamining激活之后你安装的所有库都会装在这个环境里不会影响其他项目。如果你不想用Anaconda也可以直接装Python然后用pip来管理库。pip是Python自带的包管理工具用法也很简单pip install pandas numpy matplotlib seaborn scikit-learn这行命令会一次性把咱们后面要用的库都装上。我建议你至少装这几个后面用到别的再单独装。注意如果你同时装了Anaconda和原生Python可能会出现命令冲突的情况。我的建议是只用其中一个不要混着用。用Anaconda就用conda install用原生Python就用pip install。3.2 Jupyter Notebook数据挖掘的最佳搭档装好环境之后你需要一个写代码的地方。我强烈推荐Jupyter Notebook它允许你把代码、运行结果、文字说明放在同一个文档里特别适合做数据探索和分析。在激活的conda环境里输入jupyter notebook浏览器会自动打开一个页面你可以在里面新建一个Notebook然后开始写代码。每个代码块叫一个cell按ShiftEnter可以运行当前cell并跳到下一个。Jupyter的好处是你可以一段一段地运行代码随时看到中间结果。比如你读入数据之后先看看前几行长什么样再决定下一步怎么处理。这种交互式的体验比一次性写完整个脚本再运行要方便得多。如果你更喜欢用IDEPyCharm和VS Code也都有Jupyter插件可以在IDE里直接编辑Notebook。我个人习惯用VS Code因为它的代码补全和调试功能比较强。3.3 验证环境是否配置成功装完之后咱们跑一段简单的代码验证一下。在Jupyter里新建一个Notebook输入以下代码import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn import datasets print(pandas版本:, pd.__version__) print(numpy版本:, np.__version__) print(所有库导入成功)运行之后如果能看到版本号输出说明环境没问题。如果报错说找不到某个库那就回到上一步用conda或pip重新安装。这里我多说一句版本号很重要。不同版本的库API可能会有变化。比如pandas 2.0之后有些函数的默认参数变了如果你照着老教程写代码可能会报错。所以遇到问题的时候先看看你的库版本是多少再去查对应的文档。4. 认识数据的第一个动作把数据读进来4.1 数据从哪里来做数据挖掘第一步永远是拿到数据。数据来源无非这么几种文件CSV、Excel、JSON、数据库MySQL、PostgreSQL、API接口、网页抓取。对于初学者来说最常见的是CSV文件因为它格式简单、通用性强。咱们这个系列会用一个模拟的电商销售数据集来演示。这个数据集包含以下字段字段名含义数据类型order_id订单编号字符串customer_id客户编号字符串order_date下单日期日期product_category商品类别字符串quantity购买数量整数unit_price单价浮点数total_amount总金额浮点数payment_method支付方式字符串city城市字符串这个数据集是我自己构造的目的是模拟真实场景中可能遇到的各种数据问题比如缺失值、异常值、格式不一致等。你可以自己用Excel造一份类似的数据或者从网上的公开数据集里找一个结构类似的。4.2 用Pandas读取CSV文件假设你已经把数据保存为sales_data.csv放在和Notebook同一个目录下。读取的代码如下import pandas as pd df pd.read_csv(sales_data.csv) print(df.shape) print(df.head())df.shape会返回一个元组比如(1000, 9)表示有1000行、9列。df.head()会显示前5行让你快速看一眼数据长什么样。这里有个小技巧如果数据量比较大head()默认显示5行你可以传参数改成10行或20行。如果想看最后几行用df.tail()。如果想随机抽样看几行用df.sample(5)。读取CSV的时候有几个参数经常用到encoding指定文件编码中文数据常用utf-8或gbksep指定分隔符默认是逗号如果是制表符就用\theader指定哪一行是列名默认是第0行usecols只读取部分列比如usecols[order_id, total_amount]dtype指定列的数据类型比如dtype{order_id: str}我踩过的一个坑是有些CSV文件里订单编号是纯数字pandas会自动把它读成整数导致前面的0丢失。比如00123变成123。这时候就需要用dtype参数强制指定为字符串。4.3 读取Excel和其他格式除了CSVExcel也是很常见的数据格式。读取Excel需要安装openpyxl库pip install openpyxl然后这样读df pd.read_excel(sales_data.xlsx, sheet_nameSheet1)sheet_name参数指定要读取的工作表名称也可以传索引号比如sheet_name0表示第一个工作表。如果数据在数据库里可以用pd.read_sql()配合SQLAlchemy来读取。如果数据是JSON格式用pd.read_json()。总之pandas几乎支持所有常见的数据格式你只需要记住对应的函数名就行。提示读取数据之后第一件事永远是看一眼数据。不要急着做分析先确认数据读进来是对的。我见过有人读Excel的时候没指定sheet_name结果读到了空白页后面所有分析都是错的。5. 把数据看透探索性数据分析的实操方法5.1 先看整体形状、类型、缺失情况数据读进来之后咱们要做的第一件事是“体检”。就像你去医院体检一样先量身高体重、测血压看看各项指标是否正常。数据体检主要看三个方面形状、类型、缺失情况。print(数据形状:, df.shape) print(\n数据类型:) print(df.dtypes) print(\n缺失值统计:) print(df.isnull().sum())df.shape告诉你数据有多少行多少列。df.dtypes告诉你每一列是什么类型。df.isnull().sum()告诉你每一列有多少个缺失值。这三行代码跑完你心里就有底了。比如你发现total_amount列有50个缺失值那后面就要决定是删掉这些行还是填充。如果发现order_date的类型是object而不是datetime那就要转换类型。我个人的习惯是每次拿到新数据先跑这三行把结果记下来。后面做任何处理之前都回头看看这些指标有没有变化。5.2 再看细节描述性统计和分布整体看完之后咱们要看细节。对于数值型列用describe()看统计量print(df.describe())这会输出每列的计数、均值、标准差、最小值、25%分位数、中位数、75%分位数、最大值。这些数字能告诉你很多信息。比如quantity的最小值是负数那肯定有问题因为购买数量不可能是负的。再比如total_amount的最大值特别大可能是异常值。对于类别型列用value_counts()看每个类别出现的次数print(df[product_category].value_counts()) print(df[payment_method].value_counts())这能帮你发现数据是否均衡。比如你发现90%的订单都是“电子产品”类别那后面做分类模型的时候就要注意类别不平衡的问题。5.3 用可视化把数据“画”出来数字看多了容易麻木画成图就直观多了。咱们用Matplotlib和Seaborn来画几个基础图。先看数值型变量的分布import matplotlib.pyplot as plt import seaborn as sns fig, axes plt.subplots(1, 3, figsize(15, 4)) sns.histplot(df[quantity], bins20, axaxes[0]) axes[0].set_title(购买数量分布) sns.histplot(df[unit_price], bins20, axaxes[1]) axes[1].set_title(单价分布) sns.histplot(df[total_amount], bins20, axaxes[2]) axes[2].set_title(总金额分布) plt.tight_layout() plt.show()直方图能告诉你数据的分布形状。如果某个变量严重右偏大部分值很小少数值很大后面可能需要做对数变换。再看类别型变量的分布fig, axes plt.subplots(1, 2, figsize(12, 4)) df[product_category].value_counts().plot(kindbar, axaxes[0]) axes[0].set_title(商品类别分布) df[payment_method].value_counts().plot(kindbar, axaxes[1]) axes[1].set_title(支付方式分布) plt.tight_layout() plt.show()柱状图能让你一眼看出哪些类别是主流哪些是长尾。最后看变量之间的关系plt.figure(figsize(8, 6)) sns.heatmap(df[[quantity, unit_price, total_amount]].corr(), annotTrue, cmapcoolwarm, fmt.2f) plt.title(数值变量相关性热力图) plt.show()热力图能告诉你哪些变量之间有强相关。比如quantity和total_amount通常会有正相关因为买得越多总金额越高。如果发现两个变量相关性极高比如0.95以上那可能其中一个就是另一个算出来的建模时要注意去掉冗余特征。注意画图的时候中文可能会显示成方块。需要在代码开头加两行配置plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False第一行指定中文字体第二行解决负号显示问题。如果你用的是Mac字体名可能不一样可以换成Arial Unicode MS。6. 数据清洗把脏数据变成能用的数据6.1 缺失值处理的几种策略缺失值是数据挖掘中最常见的问题。处理缺失值之前先要搞清楚缺失的原因。是随机缺失还是因为某些原因系统性缺失比如收入字段缺失可能是因为高收入人群不愿意填这就是系统性缺失。处理缺失值的策略主要有这几种删除如果缺失比例很低比如不到5%可以直接删掉这些行。用df.dropna()。填充均值/中位数/众数数值型用均值或中位数类别型用众数。用df.fillna()。填充固定值比如用0填充或者用“未知”填充。插值对于时间序列数据可以用前后值插值。用模型预测用其他列作为特征训练一个模型来预测缺失值。这种方法比较复杂适合缺失比例较高且其他列信息丰富的情况。我个人的经验是如果缺失比例低于5%直接删如果在5%到30%之间用中位数或众数填充如果超过30%就要慎重考虑这一列是否还有保留价值。# 删除缺失值 df_clean df.dropna() # 用中位数填充数值列 df[quantity] df[quantity].fillna(df[quantity].median()) # 用众数填充分类列 df[payment_method] df[payment_method].fillna(df[payment_method].mode()[0])6.2 异常值检测与处理异常值是指那些明显偏离正常范围的值。比如购买数量是-5或者单价是100万。异常值可能是录入错误也可能是真实的极端情况。处理之前要先判断原因。检测异常值常用的方法有两种方法一IQR法Q1 df[total_amount].quantile(0.25) Q3 df[total_amount].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[total_amount] lower_bound) | (df[total_amount] upper_bound)] print(f发现{len(outliers)}个异常值)IQR法的逻辑是如果某个值小于Q1-1.5倍IQR或者大于Q31.5倍IQR就认为是异常值。这个方法不假设数据服从正态分布比较稳健。方法二Z-score法from scipy import stats z_scores stats.zscore(df[total_amount]) outliers df[abs(z_scores) 3] print(f发现{len(outliers)}个异常值)Z-score法的逻辑是如果某个值偏离均值超过3倍标准差就认为是异常值。这个方法假设数据近似正态分布对极端值比较敏感。处理异常值的方式也有几种删掉、替换成边界值、或者单独标记出来。我一般会先看看这些异常值是不是真实数据如果是录入错误就删掉如果是真实情况就保留但做标记。6.3 数据类型转换和格式统一数据类型不对后面分析就会出问题。最常见的是日期列被读成了字符串。转换方法df[order_date] pd.to_datetime(df[order_date])转换之后你就可以用.dt访问器来提取年、月、日、星期等信息df[year] df[order_date].dt.year df[month] df[order_date].dt.month df[day_of_week] df[order_date].dt.dayofweek另一个常见问题是字符串列有多余的空格或大小写不一致。比如“北京”和“北京 ”会被当成两个不同的类别。处理方法df[city] df[city].str.strip() df[city] df[city].str.lower()str.strip()去掉首尾空格str.lower()统一转成小写。如果是中文lower()不影响但strip()很有用。7. 常见问题与排查技巧实录7.1 数据读取阶段的典型报错报错一FileNotFoundError这个报错的意思是找不到文件。原因通常是路径写错了。如果你用的是相对路径要确保文件在当前工作目录下。可以用import os; print(os.getcwd())看看当前目录是什么。报错二UnicodeDecodeError这个报错的意思是编码不对。中文CSV文件常用gbk或gb18030编码可以试试df pd.read_csv(sales_data.csv, encodinggbk)如果还不行试试gb18030它的字符集更全。报错三ParserError这个报错通常是分隔符不对或者某一行字段数量不一致。可以试试用sep\t或者enginepython。7.2 数据探索阶段的常见困惑困惑一describe()输出的计数和shape对不上这说明有缺失值。describe()的count只统计非缺失值而shape统计的是所有行。如果count小于行数就说明有缺失。困惑二value_counts()输出的类别太多如果某个类别型列有几百个不同的值画柱状图会很难看。这时候可以只显示前10个df[city].value_counts().head(10).plot(kindbar)或者把长尾类别合并成“其他”top_cities df[city].value_counts().head(10).index df[city_grouped] df[city].apply(lambda x: x if x in top_cities else 其他)困惑三相关性热力图全是深色如果变量很多热力图会显得很乱。可以只选几个关键变量或者用mask参数隐藏上半三角。7.3 数据清洗阶段的避坑指南坑一在原始数据上直接修改我强烈建议你养成一个好习惯读入数据后先复制一份所有清洗操作都在副本上做。这样如果改错了还能回到原始数据重新来。df_raw pd.read_csv(sales_data.csv) df df_raw.copy()坑二用均值填充所有缺失值均值填充只适合数值型且分布比较均匀的情况。如果数据严重偏态均值会被极端值拉偏这时候用中位数更合适。类别型数据绝对不能用均值填充。坑三忽略缺失值本身的含义有时候缺失本身就是一种信息。比如“优惠券编号”缺失可能意味着这单没有用优惠券。这时候填充“无”比填充众数更合理。坑四异常值删得太随意删异常值之前一定要想清楚这个异常值是错误还是真实情况如果是真实的高价值客户删掉就损失了重要信息。我一般会先标记出来分析完之后再决定是否删除。7.4 常见问题速查表问题现象可能原因解决方法读取CSV报编码错误文件编码不是UTF-8尝试encodinggbk或gb18030日期列无法提取月份列类型是object不是datetime用pd.to_datetime()转换数值列被读成字符串列中有特殊字符或空格用pd.to_numeric(errorscoerce)转换类别数量太多数据未清洗或长尾类别多合并长尾类别或只显示Top N热力图显示不全变量太多或画布太小调整figsize或只选部分变量中文显示为方块未配置中文字体设置plt.rcParams[font.sans-serif]缺失值填充后分布改变填充策略不合适对比填充前后的分布图异常值检测结果太多阈值设置过严调整IQR倍数或Z-score阈值8. 我个人的一些实操体会说了这么多方法和步骤最后分享几个我在实际工作中总结出来的经验都是踩过坑之后才明白的。第一个体会是慢就是快。很多新手拿到数据就急着跑模型结果模型效果不好回头返工。我现在的习惯是在认识数据这个阶段花的时间至少占整个项目的30%。把数据看透了后面建模反而快。就像盖房子地基打牢了上面怎么盖都稳。第二个体会是画图比看数字重要。数字能告诉你均值是多少、标准差是多少但画成图之后你才能看到分布的形状、异常值的具体位置、变量之间的非线性关系。我经常跟团队里的人说你如果只看了describe()就觉得自己了解数据了那大概率会漏掉重要信息。第三个体会是记录每一步操作。我在Jupyter里做分析的时候每个关键步骤都会用Markdown cell写清楚我做了什么、为什么这么做、结果是什么。这样过了一周再回来看还能快速回忆起当时的思路。而且如果后面发现问题也能快速定位是哪一步出的错。第四个体会是不要追求一步到位。数据清洗不是一次性的工作而是迭代的过程。你可能先处理了缺失值然后发现异常值处理完异常值又发现类型不对。这很正常不要想着一次性把所有问题都解决。先处理最明显的跑一遍分析看看结果是否合理再决定下一步。第五个体会是多问业务问题。技术只是手段业务才是目的。你在清洗数据的时候要多问自己这个字段在业务上是什么意思这个异常值在业务上合理吗这个缺失值在业务上意味着什么想清楚这些问题你的数据处理才会更有方向。这个系列的第一篇就到这里。下一篇咱们会讲数据预处理的具体操作包括数据标准化、归一化、编码转换这些内容。如果你跟着这篇文章把环境搭好了、数据读进来了、基本探索也做了那后面的内容会顺很多。如果你在操作过程中遇到什么问题欢迎在评论区交流我看到都会回复。