简介:这份资源面向零基础或希望系统入门数据科学的读者,围绕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 python=3.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", encoding="utf-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_name="Sheet1"),sheet_name不写默认读第一个sheet。
3.2 缺失值:先判断类型再决定填还是删
缺失值分三种:完全随机缺失、随机缺失、非随机缺失。实操中不可能严格区分,但可以用缺失比例和业务含义做决策。我一般先跑一个缺失概览:
# 计算每列缺失比例 missing_ratio = df.isnull().sum() / len(df) # 只显示有缺失的列,按比例降序 missing_ratio[missing_ratio > 0].sort_values(ascending=False)逻辑说明: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(columns=missing_ratio[missing_ratio > threshold].index)逻辑说明:median()对异常值不敏感,比均值更稳;mode()[0]取众数第一个值,因为众数可能返回多个;drop(columns=...)按列名删除。
参数说明:fillna不会修改原DataFrame,除非加inplace=True,但我建议显式赋值,避免链式操作警告。
3.3 异常值:用IQR和业务规则双保险
异常值检测常用IQR法:低于Q1-1.5IQR或高于Q3+1.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", ascending=False) 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_output=False, handle_unknown="ignore") encoded = ohe.fit_transform(df[["city"]]) encoded_df = pd.DataFrame(encoded, columns=ohe.get_feature_names_out(["city"])) df = pd.concat([df, encoded_df], axis=1)逻辑说明:LabelEncoder按字母顺序映射整数,所以只适合有序类别;OneHotEncoder的sparse_output=False返回稠密数组,方便转DataFrame;handle_unknown="ignore"保证预测时遇到新类别不报错。
参数说明:get_feature_names_out生成列名如city_北京、city_上海;concat(axis=1)按列拼接。注意独热编码后列数会膨胀,如果类别超过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_size=0.2, random_state=42, stratify=y ) # 训练 model = LogisticRegression(max_iter=1000) model.fit(X_train, y_train) # 预测与评估 y_pred = model.predict(X_test) print(classification_report(y_test, y_pred))逻辑说明:stratify=y保证训练集和测试集的标签比例一致,避免某类样本过少;max_iter=1000防止迭代不收敛;classification_report输出精确率、召回率、F1。
参数说明:test_size=0.2是常见划分比例,数据量小可以调到0.3;random_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检测编码,或先用encoding="gbk"试,不行换utf-8。最稳的办法是拿到数据时问清楚来源系统的导出编码。
5.3 内存溢出:大数据集不要全量读
现象:读一个2GB的CSV直接卡死。
原因:pandas默认全量加载到内存,内存不够就崩。
解决:用chunksize分块读:
chunk_list = [] for chunk in pd.read_csv("big.csv", chunksize=100000): # 每块做过滤,只保留需要的行 filtered = chunk[chunk["amount"] > 100] chunk_list.append(filtered) df = pd.concat(chunk_list, ignore_index=True)参数说明:chunksize=100000表示每块10万行,根据内存调整;ignore_index=True重新生成索引,避免重复。
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(columns=config.get("drop_cols", [])) # 日期解析 for col in config.get("parse_dates", []): df[col] = pd.to_datetime(df[col], errors="coerce") # 中位数填充 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", [])保证没配置的步骤跳过,不会报KeyError;errors="coerce"让无法解析的日期变成NaT而不是抛异常,方便后续统计缺失。
参数说明:这个模式可以继续扩展,比如加"winsorize": ["amount"]做缩尾处理,加"log_transform": ["amount"]做对数变换。核心思想是“配置驱动”,而不是“代码驱动”。
我自己的教训是:早期做项目时,清洗代码和建模代码混在一个Notebook里,改一个参数要重跑全部单元格,浪费大量时间。后来强制自己把清洗抽成独立函数,建模只读清洗后的数据,效率翻倍。另一个习惯是每次清洗后必跑df.info()和df.describe(),确认行数、类型、分布没有意外变化——这个动作花10秒,能省下后面几小时的排查。
希望帮到你。
本文还有配套的精品资源,点击获取