YAOTU INSIGHTS

Python数据分析与挖掘入门:从环境搭建到数据清洗的完整流程

Python数据分析与挖掘入门:从环境搭建到数据清洗的完整流程
简介这份资源面向零基础或希望系统入门数据科学的读者围绕Python数据分析与挖掘的完整流程展开从语法基础逐步过渡到建模实战帮助解决入门阶段知识零散、缺乏动手路径的问题。包内以PPT文档与源码实例为主压缩包约106.92MB文件总数暂未提供整体结构便于按章节对照学习。内容覆盖Python快速入门、numpy多维数组与矩阵运算、pandas的DataFrame清洗与合并切片以及线性回归、决策树、KNN、SVM、朴素贝叶斯与K-means聚类等经典模型并延伸至缺失值处理、异常值检测、标准化归一化、交叉验证与网格搜索调优等环节。已有1490人学习下载适合学术研究或职业发展场景下通过文档与代码结合的方式亲手实践逐步建立从数据预处理到模型评估的完整分析思路。1. 从零开始学Python数据分析与挖掘为什么你装了pandas还是跑不通第一个项目很多人学Python数据分析与挖掘的路径是这样的花两天装好Anaconda跟着教程敲了import pandas as pd然后打开一份CSV发现列名带空格、日期是字符串、缺失值长得五花八门瞬间卡住。这不是你笨是绝大多数入门教程把“环境搭好”等同于“能干活”中间缺了一整层——数据从脏到干净、从干净到有结论的完整链路。这个方向真正要解决的问题不是“学会某个库的语法”而是建立一套可复用的处理流程拿到一份陌生数据知道先看什么、再怎么洗、用什么模型验证、结果怎么解释。适合两类人一是有点Python基础但没做过完整数据项目的开发者二是做运营、产品、财务等岗位想用代码替代手工Excel的人。下面按真实做项目的顺序拆开讲每一步都给能直接跑的代码和参数说明。2. 环境与工具链把“能跑”变成“跑得稳”2.1 为什么我推荐conda而不是裸pip新手最容易翻车的地方是包版本冲突。pandas、numpy、scikit-learn之间有严格的依赖关系裸pip装完经常出现numpy版本不兼容导致import直接报错。conda的优势是它同时管理Python解释器和二进制依赖尤其在Windows上很多科学计算包需要编译好的C扩展conda直接给预编译版本。我一般会建一个独立环境而不是装在base里# 创建名为dataenv的环境指定Python 3.10 conda create -n dataenv python3.10 # 激活环境 conda activate dataenv # 一次性装齐核心四件套 conda install pandas numpy matplotlib scikit-learn jupyter逻辑说明-n dataenv给环境起名方便多个项目隔离指定3.10是因为部分老教程用的3.7已经停止维护而3.12某些库的wheel还没跟上。装完后用conda list确认版本重点看pandas是否在1.5以上、numpy是否在1.23以上。参数说明如果你所在网络下载慢可以配置国内镜像源但注意镜像源要选可信的配置命令是conda config --add channels 镜像地址这里不展开具体地址按你所在环境的IT规范来。2.2 验证环境是否真的可用装完不代表能用。我见过太多人import pandas成功但一读Excel就报Missing optional dependency openpyxl。所以建完环境后跑一段验证脚本import pandas as pd import numpy as np import sklearn import matplotlib # 打印版本确认没有警告 print(pandas:, pd.__version__) print(numpy:, np.__version__) print(sklearn:, sklearn.__version__) # 构造一个最小DataFrame测试基本运算 df pd.DataFrame({a: [1, 2, 3], b: [4.0, np.nan, 6.0]}) print(df.describe()) print(缺失值统计:\n, df.isnull().sum())逻辑说明这段代码同时测了导入、版本、DataFrame构造、统计描述、缺失值检测五个环节。如果describe()能输出说明numpy的底层计算正常如果isnull().sum()能输出说明pandas的缺失值机制正常。参数说明describe()默认只统计数值列isnull()返回布尔矩阵.sum()按列求和得到每列缺失数量。这两个是后面数据清洗阶段最常用的诊断命令建议背下来。2.3 Jupyter的两种用法与选择Jupyter Notebook适合探索性分析一边写代码一边看输出JupyterLab适合多文件项目有文件树和终端。我的习惯是前期探索用Notebook确定流程后把稳定代码抽成.py脚本用python script.py跑批处理。启动命令jupyter lab --notebook-dir./projects--notebook-dir指定工作目录避免默认目录里堆满临时文件。启动后浏览器会自动打开如果没打开终端里会打印一个带token的地址复制到浏览器即可。注意这个token是本地访问凭证不要分享给他人。3. 数据清洗80%的时间花在这里但没人告诉你具体怎么花3.1 读数据时的四个必设参数很多人读CSV只写pd.read_csv(file.csv)然后发现中文乱码、第一列变成了索引、日期列是object类型。我一般会显式写全参数df pd.read_csv( sales.csv, encodingutf-8, # 中文环境常用utf-8或gbk sep,, # 分隔符tsv文件改成\t parse_dates[order_date], # 指定日期列自动解析 dtype{store_id: str} # 门店编号保持字符串避免前导零丢失 )逻辑说明encoding不指定时Windows默认用gbk读遇到utf-8文件就乱码parse_dates让pandas直接转成datetime类型省去后面pd.to_datetime的步骤dtype强制某些列不参与类型推断比如编号“001”如果被推断成整数就变成1后续关联全错。参数说明parse_dates接受列名或列名列表dtype传字典键是列名值是目标类型。如果读Excel用pd.read_excel(file.xlsx, sheet_nameSheet1)sheet_name不写默认读第一个sheet。3.2 缺失值先判断类型再决定填还是删缺失值分三种完全随机缺失、随机缺失、非随机缺失。实操中不可能严格区分但可以用缺失比例和业务含义做决策。我一般先跑一个缺失概览# 计算每列缺失比例 missing_ratio df.isnull().sum() / len(df) # 只显示有缺失的列按比例降序 missing_ratio[missing_ratio 0].sort_values(ascendingFalse)逻辑说明isnull().sum()得到每列缺失数量除以总行数得到比例。排序后一眼看出哪些列缺失严重。参数说明如果某列缺失超过60%且不是关键字段我一般直接删列如果缺失在5%以内数值列用中位数填充类别列用众数填充介于两者之间考虑用模型预测填充但新手阶段建议先用简单方法跑通流程。填充示例# 数值列用中位数填充 df[amount] df[amount].fillna(df[amount].median()) # 类别列用众数填充 df[city] df[city].fillna(df[city].mode()[0]) # 删除缺失超过60%的列 threshold 0.6 df df.drop(columnsmissing_ratio[missing_ratio threshold].index)逻辑说明median()对异常值不敏感比均值更稳mode()[0]取众数第一个值因为众数可能返回多个drop(columns...)按列名删除。参数说明fillna不会修改原DataFrame除非加inplaceTrue但我建议显式赋值避免链式操作警告。3.3 异常值用IQR和业务规则双保险异常值检测常用IQR法低于Q1-1.5IQR或高于Q31.5IQR的值视为异常。但纯统计方法会误杀真实的大额订单所以必须结合业务规则。Q1 df[amount].quantile(0.25) Q3 df[amount].quantile(0.75) IQR Q3 - Q1 lower Q1 - 1.5 * IQR upper Q3 1.5 * IQR # 标记异常不直接删除 df[is_outlier] ((df[amount] lower) | (df[amount] upper)).astype(int) # 查看异常样本 print(df[df[is_outlier] 1][[order_id, amount]].head(10))逻辑说明quantile(0.25)和quantile(0.75)分别取四分位数astype(int)把布尔转成0/1标记方便后续筛选。标记而不是直接删是因为有些异常值是真实业务事件比如促销日的大单。参数说明1.5是经验系数也可以改成3.0让判定更宽松。如果数据明显右偏可以先做对数变换再算IQR。4. 从描述统计到建模一条能跑通的最小链路4.1 分组聚合用groupby回答业务问题数据分析的核心动作是“按某个维度分组看另一个维度的表现”。比如按门店看销售额result df.groupby(store_id).agg( total_amount(amount, sum), avg_amount(amount, mean), order_count(order_id, count) ).reset_index() # 按总销售额降序 result result.sort_values(total_amount, ascendingFalse) print(result.head())逻辑说明agg里用元组指定“新列名(原列名, 聚合函数)”reset_index()把分组键从索引变回普通列方便后续操作sort_values排序后取头部。参数说明常用聚合函数有sum、mean、count、max、min、nunique去重计数。如果要多个聚合可以传列表比如(amount, [sum, mean])。4.2 特征工程把原始列变成模型能吃的列建模前要把类别列转成数值。两种方式标签编码每个类别一个整数和独热编码每个类别一个0/1列。类别之间有序用标签编码无序用独热编码。from sklearn.preprocessing import LabelEncoder, OneHotEncoder # 有序类别标签编码 le LabelEncoder() df[level_encoded] le.fit_transform(df[member_level]) # 无序类别独热编码 ohe OneHotEncoder(sparse_outputFalse, handle_unknownignore) encoded ohe.fit_transform(df[[city]]) encoded_df pd.DataFrame(encoded, columnsohe.get_feature_names_out([city])) df pd.concat([df, encoded_df], axis1)逻辑说明LabelEncoder按字母顺序映射整数所以只适合有序类别OneHotEncoder的sparse_outputFalse返回稠密数组方便转DataFramehandle_unknownignore保证预测时遇到新类别不报错。参数说明get_feature_names_out生成列名如city_北京、city_上海concat(axis1)按列拼接。注意独热编码后列数会膨胀如果类别超过50个考虑用目标编码替代。4.3 建模与评估先跑通再调优以分类任务为例用逻辑回归跑一个基线from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report # 准备特征和标签 X df[[amount, order_count, level_encoded]] y df[is_churn] # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 训练 model LogisticRegression(max_iter1000) model.fit(X_train, y_train) # 预测与评估 y_pred model.predict(X_test) print(classification_report(y_test, y_pred))逻辑说明stratifyy保证训练集和测试集的标签比例一致避免某类样本过少max_iter1000防止迭代不收敛classification_report输出精确率、召回率、F1。参数说明test_size0.2是常见划分比例数据量小可以调到0.3random_state固定随机种子保证每次划分结果一致方便复现。注意如果评估指标远高于预期先检查是否有标签泄漏——比如特征里混入了预测目标相关的列。5. 避坑与排查那些让我重跑一整天的错误5.1 SettingWithCopyWarning链式索引的坑现象执行df[df[a] 1][b] 0时报警告且赋值没生效。原因df[df[a] 1]返回的是副本还是视图不确定pandas无法保证赋值写回原DataFrame。解决用.loc一步完成df.loc[df[a] 1, b] 0。如果确实要操作副本先.copy()。5.2 中文乱码编码不是猜出来的现象读CSV后列名变成\xe6\x97\xa5\xe6\x9c\x9f。原因文件实际编码与encoding参数不一致。解决用chardet检测编码或先用encodinggbk试不行换utf-8。最稳的办法是拿到数据时问清楚来源系统的导出编码。5.3 内存溢出大数据集不要全量读现象读一个2GB的CSV直接卡死。原因pandas默认全量加载到内存内存不够就崩。解决用chunksize分块读chunk_list [] for chunk in pd.read_csv(big.csv, chunksize100000): # 每块做过滤只保留需要的行 filtered chunk[chunk[amount] 100] chunk_list.append(filtered) df pd.concat(chunk_list, ignore_indexTrue)参数说明chunksize100000表示每块10万行根据内存调整ignore_indexTrue重新生成索引避免重复。5.4 模型指标虚高时间序列不能用随机划分现象用train_test_split划分时间数据准确率0.95上线后掉到0.6。原因随机划分让未来数据泄露到训练集。解决按时间切分比如前80%做训练后20%做测试split_idx int(len(df) * 0.8) train df.iloc[:split_idx] test df.iloc[split_idx:]5.5 版本不兼容pickle模型跨环境加载失败现象本地训练好的模型部署到服务器joblib.load报错。原因训练和部署的scikit-learn版本不一致。解决用conda env export environment.yml导出完整环境部署时用同一文件重建环境。或者用ONNX格式跨框架导出。6. 一个能复用的技巧把清洗流程写成可配置的管道新手最容易犯的错是每次拿到新数据都重写一遍清洗代码。我的习惯是把清洗步骤写成函数用字典配置参数这样换一份数据只改配置不改逻辑。def clean_data(df, config): config示例: { drop_cols: [备注], fill_median: [amount], fill_mode: [city], parse_dates: [order_date] } # 删列 df df.drop(columnsconfig.get(drop_cols, [])) # 日期解析 for col in config.get(parse_dates, []): df[col] pd.to_datetime(df[col], errorscoerce) # 中位数填充 for col in config.get(fill_median, []): df[col] df[col].fillna(df[col].median()) # 众数填充 for col in config.get(fill_mode, []): df[col] df[col].fillna(df[col].mode()[0]) return df # 使用 config { drop_cols: [internal_note], fill_median: [amount, quantity], fill_mode: [city, channel], parse_dates: [order_date, deliver_date] } df_clean clean_data(df_raw, config)逻辑说明config.get(key, [])保证没配置的步骤跳过不会报KeyErrorerrorscoerce让无法解析的日期变成NaT而不是抛异常方便后续统计缺失。参数说明这个模式可以继续扩展比如加winsorize: [amount]做缩尾处理加log_transform: [amount]做对数变换。核心思想是“配置驱动”而不是“代码驱动”。我自己的教训是早期做项目时清洗代码和建模代码混在一个Notebook里改一个参数要重跑全部单元格浪费大量时间。后来强制自己把清洗抽成独立函数建模只读清洗后的数据效率翻倍。另一个习惯是每次清洗后必跑df.info()和df.describe()确认行数、类型、分布没有意外变化——这个动作花10秒能省下后面几小时的排查。希望帮到你。本文还有配套的精品资源点击获取