news 2026/9/24 19:10:47

大数据分析实战:从数据清洗到concat合并的完整流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大数据分析实战:从数据清洗到concat合并的完整流程

1. 大数据分析的“全局观”:从数据到结论到底走几步

1.1 我在Day46里重新理解的“大数据”

写这个每日总结系列到今天,已经是第46天。前面45天我聊过数据抓取、清洗、可视化、机器学习基础,但说实话,直到昨天完整跑完一个“旅游网站订单数据分析”的小项目,我才真正想明白一件事:所谓大数据分析,核心不是“大”,而是“分析”

很多人被“大数据”三个字吓住,觉得一定要上Hadoop、Spark才叫大数据。我一开始也这么认为,后来发现,真正决定分析价值的,是你有没有能力把一堆乱糟糟的数据,变成一张清晰的表格,再变成一套能指导业务的结论。我用到的工具,就是Python配上pandas、NumPy这些常见库,数据量也就几十万行。可这不妨碍它是一次标准的大数据分析流程。

那大数据分析和普通数据分析到底差在哪?我的理解是:分析流程的结构化程度要求更高,数据准备阶段占的时间更长,坑也更多。比如你拿到的数据可能是从多个渠道拼出来的,格式不同、字段名不同、时间格式不统一,甚至同一批数据里混着Excel导出的科学计数法——这些都要在进入分析环节之前处理干净。Day46这一天的学习,我基本就泡在“处理数据”这件事上。

1.2 一套能落地的分析流程框架

我习惯把一次完整的数据分析项目拆成五个阶段,这套思路适用于大部分业务场景:

  • 需求定义:搞清楚你要回答什么问题,而不是先找数据。
  • 数据获取:爬虫抓取、数据库导出、第三方API、手工整理都算。
  • 数据清洗:处理缺失值、重复值、异常值、类型错误、格式不统一。
  • 数据合并与加工:把多张表关联起来,生成新字段,这一步经常用到concat。
  • 分析与呈现:聚合统计、可视化,最终形成结论。

这五步里,数据清洗和合并往往占掉整个项目70%的时间。原因很简单,真实数据永远是脏的。比如我从某个旅游网站上抓下来的数据,订单金额字段里混着“¥1,299”、“1299元”、“1,299.00元”三种格式;日期的写法有“2024-03-15”也有“2024/3/15”;更离谱的是,有一列明明应该是整数ID,结果某一行变成了“3.21E+12”——这就是后面要说的科学计数法问题。遇到这些,第一步永远是写清洗逻辑,把数据标准化。

所以这篇总结我打算沿着“抓取-清洗-合并-分析”这条线,把Day46做旅游网站数据分析时的思考和实操记录下来,尤其是concat函数的各种细节和几个容易踩的坑。

2. 数据从哪来:抓取与接入的几种常见姿势

2.1 旅游网站场景下的数据抓取思路

我这次用的数据,一部分来自一个旅游网站的公开页面,一部分来自本地数据库导出。抓取环节我没有上Selenium这种重型工具,而是先用requests直接请求接口,因为很多现代网站的数据都是通过JSON接口异步加载的。你在浏览器里能看到列表页,但数据其实是后台接口返回的JSON,直接在开发者工具里找到这个接口,比解析HTML高效太多。

举个实际例子。我要抓某个目的地的酒店列表,打开开发者工具,切到Network面板,刷新页面,看到返回数据的XHR请求,复制它的URL。一般这种URL里会带上城市ID、页码、每页条数这些参数。我用requests模拟请求,加一个常见的User-Agent头,就能拿到JSON数据。操作大概是这样的:

import requests url = "https://example.com/api/hotel/list" params = { "city_id": 1024, "page": 1, "page_size": 50 } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } resp = requests.get(url, params=params, headers=headers, timeout=10) data = resp.json()

拿到JSON之后,先把它转成DataFrame看一眼结构。这一步的目标不是立刻做分析,而是搞清楚有哪些字段、哪些字段为空、字段类型是什么。我一般会打印列名和行数,再用df.info()看整体概况。这里有个小经验:第一次抓数据,先只抓一页,确认结构无误,再循环抓全量。否则接口参数写错,抓了几千条全是一个城市的,后面清洗白干。

2.2 数据接入的格式与工具选型

不管数据是从接口抓来的、数据库导出的,还是Excel手工整理的,最后都要统一到DataFrame里。这个阶段我用到两个高频操作:pd.read_csv()pd.read_excel()。如果是从MySQL这类数据库导数据,可以用pymysql或者直接在可视化工具里导出CSV再读。

这里顺便提一个我踩过多次的坑:用可视化工具导出的CSV,编码经常是GBK或者带BOM的UTF-8,直接用pandas默认参数读会乱码。所以我的通用读法是这样的:

import pandas as pd df = pd.read_csv("hotel_data.csv", encoding="utf-8-sig")

utf-8-sig这个编码能自动处理BOM头,而且对Excel另存的CSV兼容性比较好。如果你发现读出来的中文还是乱,就把编码换成gbk试试。这个小细节在数据接入阶段特别实用,尤其是当你手里的文件是别人发来的,你根本不知道对方用什么编码存的。

数据接入完成后,我会习惯性地做一次“结构确认”:打印前5行、看列名清单、看每列的空值数量和类型。这一遍可能只需几分钟,但它决定了后续清洗策略怎么写。

3. 数据清洗:脏数据才是分析的真正门槛

3.1 缺失值、重复值和类型问题的处理顺序

数据清洗最容易犯的错误是“想到哪洗到哪”。如果每一列都单独处理,最后字段之间很容易产生矛盾。我自己的经验是,固定一个处理顺序,反复用,这样思路清晰,代码也容易复用。

我的顺序是:先去重,再处理缺失值,最后统一类型和格式。去重放前面,是因为重复记录会干扰缺失值的统计。比如同一个订单被插了两遍,第一遍金额为空,第二遍金额正常,如果先去重,留下的是第二遍,那这个缺失值天然就被解决了。

去重用drop_duplicates(),注意subset参数。我这次处理订单数据时,一个订单ID对应一行,所以按客户ID加订单时间两个字段一起去重:

df = df.drop_duplicates(subset=["customer_id", "order_time"])

这一步看着简单,但有个细节容易忽略:drop_duplicates()默认保留前面的行,如果后面的行数据更完整,需要加keep="last"。我的经验是,先按某个主键排序,把数据质量好的行放前面,再去重。

缺失值处理要看字段性质和缺失比例。如果缺失比例超过70%,这个字段基本可以放弃;如果缺失值集中在某一小段,可以考虑行删除;如果字段是数值型,缺失量不大,可以用中位数或均值填充。不要上来就fillna(0),把缺失值填成0会让聚合结果严重失真。比如酒店价格列,如果缺失就填0,平均价格会被拉到一个完全没参考价值的数字。

3.2 清洗实操:一个旅游订单数据的小例子

这里用一个小例子演示清洗过程。假设我拿到的订单数据有三列:订单日期、支付金额、客户城市。实际数据如下:

订单日期支付金额客户城市
2024/3/1¥1,299.00上海
2024-03-02899元北京
2024/3/21,299.00元上海
2024/03/03599成都

这短短四行就集体示范了格式不统一的问题。日期有三种写法,金额有符号、中文单位、小数位数不统一,城市空格不规则。我的清洗思路是分列处理:

import re # 日期标准化 df["订单日期"] = pd.to_datetime(df["订单日期"]) # 金额清洗:去掉符号、中文、千分位,只留数字 def clean_amount(x): s = str(x) s = s.replace("¥", "").replace("元", "").replace(",", "").strip() return float(s) df["支付金额"] = df["支付金额"].apply(clean_amount) # 城市去空格 df["客户城市"] = df["客户城市"].str.strip()

pd.to_datetime()能自动识别大部分常见日期格式,这一步非常省心。金额清洗用自定义函数加正则也行,但这里因为模式很规整,直接替换就够了。需要注意的一点是:清洗操作必须新建列或者直接覆盖原列,最好保留一份原始备份。我习惯在处理前先执行df_raw = df.copy(),万一后面发现清洗逻辑写错了,还能回头对比。

做完这些之后,建议立刻用df.dtypes检查每列类型,再用df["支付金额"].describe()看一眼数据分布。如果发现最大值是负数,或者最小值是0,那很可能清洗逻辑有漏洞,得回头查。

4. concat()函数:纵向与横向合并的核心细节

4.1 axis参数决定方向:纵向堆叠还是横向拼接

concat是pandas里最常用的合并函数之一,也是我Day46重点复盘的内容。它最核心的参数就是axisaxis=0是纵向合并,把多张表按行方向堆起来;axis=1是横向合并,把多张表按列方向并排拼起来

直观理解:纵向合并就像把两摞砖上下叠在一起,总高度变高;横向合并就像把两张桌子并排放,总宽度变宽。这个方向性问题搞不清楚,后面所有数据都会错位。

我这次遇到的具体场景是:网站订单数据按月份分成了三张表,分别是1月、2月、3月的订单。现在要把三个月的数据合并成一张季度订单表,这时候必须用纵向合并:

df_q1 = pd.concat([df_jan, df_feb, df_mar], axis=0, ignore_index=True)

注意我加了ignore_index=True。这个参数很关键,如果原表索引是0、1、2各自排列,直接concat后索引会重复,后续按索引筛选数据时会出问题。加上ignore_index=True,pandas会重新生成0到N-1的新索引,相当于把三张表彻底重排。

4.2 join、ignore_index等参数的实操对比

除了axis,concat还有几个参数在实战中特别关键。第一个是join,它控制合并时如何对待列名。

  • 纵向合并时,join="outer"会保留所有表的列;join="inner"只保留所有表都有的列。
  • 横向合并时,join="outer"会保留所有表的行(行按索引对齐);join="inner"只保留两边索引重叠的行。

我举个例子。1月订单表有“订单号、金额、城市”三列,2月订单表有“订单号、金额、城市、备注”四列。如果纵向concat不指定join,默认是outer,那1月的数据在“备注”列就会填空值。这个行为多数时候是我们想要的。但如果你明确知道两表结构完全一致,用join="inner"可以主动校验一下,只要有列名不一致,立刻报错,反而能帮你提前发现问题。

第二个是ignore_index,刚才已经提到了。第三个是keys参数,它的作用是在纵向合并时,给每块数据加一个分组标签,方便追踪数据来源:

df_all = pd.concat( [df_jan, df_feb, df_mar], axis=0, keys=["jan", "feb", "mar"] )

这样合并后,索引层级会多一层,用df_all.loc["jan"]就能单独取出1月的数据。我在做月度对比分析时很喜欢用这个参数,省得额外加一列月份字段。

第四个是横向合并时索引对齐的问题。如果你有两张表,分别是订单基本信息和订单支付信息,它们的行数一样,但顺序不一样,直接用axis=1concat会把两边的数据错位拼在一起。这时绝对不能直接concat,而应该先按公共键排序,或者用merge按订单号关联。concat的横向拼接适合“列顺序完全一致”的场景,比如两张表都按同样的订单号排序;一旦需要按字段匹配两张表,还是用merge更稳妥。

4.3 concat与merge、join的选择逻辑

很多初学者搞不清concat、merge和join的区别。我总结了一个简单的选择逻辑:

  • 需要把多张结构相同或相似的表“堆”成一张长表,用concat,axis=0。
  • 需要给一张表“额外增加列”,且这些列来自另一张表,按某个共同字段匹配,用merge。
  • DataFrame的索引对齐拼接,用join,但要确认索引语义一致。

上次毕设答辩时,有个学弟被问到“concat和merge都是合并,有什么区别”,他答得模棱两可。其实一句话就能说清:concat是“物理拼接”,不管内容是否有关联,只负责把数据按方向拼起来;merge是“逻辑关联”,按照一个或多个键把两张表的行匹配起来。理解了这个,你在选函数时就不会犹豫。

5. 分析落地与可视化:别让结果停在DataFrame里

5.1 分组聚合与指标拆解

数据清洗合并完之后,终于进入分析环节。这次旅游网站的数据,我主要想看这几个问题:不同城市的订单量分布、各月份的订单金额趋势、以及客单价是否有明显变化。

第一个问题用groupbysize()就行:

city_count = df.groupby("客户城市").size().sort_values(ascending=False)

第二个问题需要先按月分组,再对金额求和:

df["月份"] = df["订单日期"].dt.month monthly_amount = df.groupby("月份")["支付金额"].sum()

这里能看出清洗阶段标准化日期字段的价值。如果日期还是“2024/3/1”和“2024-03-02”混在一起,dt.month根本提取不了月份,聚合分析就卡住了。所以清洗不是“做完就行”,它是所有后续分析的地基。

第三个问题算客单价,直接总金额除以订单总量。这个指标适合看整体消费水平,但如果有异常大额订单,会被拉高。所以我在计算前先看一波金额分布,用quantile找出99分位线,超过这个线的订单单独标记出来,看看是不是异常数据。这一步不是必须的,但在真实项目里,异常值对决策的影响往往比你想的大得多。

5.2 可视化表达的几个习惯

分析结果光靠数字输出,说服力不够。我习惯用matplotlib快速画几张图,辅助理解数据。但可视化有几个我自己总结的习惯:

  • 能画图就画图,不要只打印数据表。
  • 图上的标题和轴标签必须写清楚,否则别人看不懂。
  • 先画分布,再画趋势,最后画对比。
  • 每张图只表达一个核心信息。

比如订单量城市分布用横向条形图最直观,因为城市名在左侧轴容易阅读;月度趋势用折线图能看出变化趋势;客单价对比用柱状图就好。下面是画城市订单量Top10的示例:

import matplotlib.pyplot as plt ax = city_count.head(10).plot(kind="barh", figsize=(10, 6)) ax.set_xlabel("订单量") ax.set_ylabel("城市") plt.title("Top10城市订单量分布") plt.gca().invert_yaxis() plt.tight_layout() plt.show()

invert_yaxis()这步是我踩坑之后加上的,因为pandas默认的barh图,数据从下往上排,第一个元素显示在最底部,视觉上看着不舒服,反转一下让最大的在最上面,读起来更自然。这种细节不影响正确性,但会影响看图人的理解效率。

6. 实操中的经典问题与避坑记录

6.1 dbeaver导出数据变成科学计数法了

这个坑我最近遇到频率特别高,必须单独拿出来讲。用DBeaver导出数据为CSV时,如果某一列是超过15位的数值ID,导出来经常会变成3.21E+12这种科学计数法格式。这其实不是DBeaver的问题,而是Excel和很多文本编辑器对超长数字的默认显示逻辑。

我第一次遇到时,直接把CSV读进pandas,那列ID全部变成了浮点数,后面关联别的表时怎么都对不上。后来排查才明白,这列是类似“321098765432123”的订单号,csv文件里存的是3.21098765432123E+14,pandas读进来就按浮点处理了。数字精度丢失,后几位全部变成0。

解决方案有两个。

方案一,在DBeaver导出时,把字段格式设置为文本或字符串,强制导出时不转科学计数法。具体操作是在导出对话框里选择格式选项,把“Data formats”里的数字格式改成文本。

方案二,如果导出的文件已经变成了科学计数法,pandas读取时指定列类型:

df = pd.read_csv("orders.csv", dtype={"order_id": str})

必须用dtype强制指定,不能加载后再astype(str),因为精度在读取那一刻已经丢了,astype(str)救不回来。如果你非要在加载后处理,可以用pd.set_option("display.float_format", lambda x: f"{x:.0f}"),但这只是显示层面修复,底层精度已经没了,关联键还是匹配不上。

这个问题的本质是:大数据处理中,精度损失是“一次性的、不可逆的”,必须在入口处堵住。不管是DBeaver导出、Excel打开还是CSV读取,遇到超长数字,第一时间就要想到“它是不是会被当作浮点数处理”,然后提前干预。

6.2 其他几个高频坑的速查

除了科学计数法,Day46实操中我还遇到几个问题,顺手整理成一张速查表:

问题现象根本原因解决方案
读CSV中文乱码文件编码是GBK,pandas默认UTF-8encoding="utf-8-sig"encoding="gbk"
concat后索引重复原表各自有0~N索引ignore_index=True
横向concat数据错位两表顺序不一致,concat按位置拼接改用merge按字段关联
金额列混有“¥”、“元”网页抽取未清洗自定义替换函数统一为标准数字
时间列格式不一致不同来源日期格式不同pd.to_datetime()统一转换
DataFrame打印科学计数法pandas默认浮点显示pd.set_option("display.float_format", lambda x: f"{x:.0f}")

这里多说一句,pd.set_option那条只是让显示好看,分析时如果需要精确输出,最好还是把列转成字符串并设置显示参数。我一般在最后的汇总统计里同时用好几种方式验证,比如用df.info()看类型、用df.head(10)看样例、用df.describe()看分布,三个一起看才安心。

还有一个很小的坑,也值得提。用pd.read_csv读取时,如果CSV里含空行,pandas默认会跳过,但跳过之后索引不是连续的,后面用reset_index(drop=True)重置一下就好。类似的细节在不同项目中反复出现,我把它们都记在当天的总结笔记里,时间长了就形成了一套自己的避坑清单。这也是写每日总结最大的价值:今天是Day46,回看第10天的自己,很多现在看来“理所当然”的操作,当时其实卡了很久

7. 多说两句:这套流程怎么迁移到你的项目里

写了这么多,最后想说点实在的。如果你也正在刷数据科学的系列课,或者正准备数据科学与大数据技术方向的毕设,我建议你别只看概念,而是完整跑一遍今天这套流程:抓一个公开网站的数据,存成CSV,清洗,合并,聚合,可视化。哪怕数据量只有几千行,也足够让你把pandas几个核心函数练熟。

我个人的体会是,数据科学的瓶颈从来不是模型,而是数据工程。很多新手一上来就研究算法调参,却忽略了对数据进行合理加工的能力。而恰恰是数据处理的基本功,决定了你能在真实项目里走多远。Day46学到的最重要的东西依然是:做分析之前,先把数据伺候明白。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 19:10:27

davhlpr.dll文件丢失怎么办?别乱下载,这样排查修复才安全

“系统找不到davhlpr.dll文件”这类弹窗,很多人第一眼看到就慌了,紧接着下意识去搜索引擎找“davhlpr.dll免费下载”。我的建议是:先别急着下载,这个文件十有八九不是你系统里本来就该有的东西,强行从网上拉一个回来&a…

作者头像 李华
网站建设 2026/9/24 19:10:24

智能家居哪个牌子好?品牌排名之外的四个关键判断指标

1. 品牌排行榜为什么不值得信任总有朋友把"智能家居哪个牌子好"这个问题抛给我,然后甩过来一张从某平台截图的品牌排行榜。我每次都跟他们说:你现在看这个榜单,等于在米其林餐厅门口问路人哪道菜好吃,参考价值有&#x…

作者头像 李华
网站建设 2026/9/24 19:09:37

遗传规划自动挖掘阿尔法因子:多因子策略代码包实战

简介:这份资源围绕遗传规划算法在多因子投资策略中生成阿尔法因子展开,面向具备一定Python基础、希望自主挖掘有效因子的量化投资者与研究者。它借助符号回归技术,先构建一组简单随机公式来刻画自变量与证券收益之间的关系,从而预…

作者头像 李华
网站建设 2026/9/24 19:08:15

Generated Code、BSW 源码、MCAL 和手写代码,到底应该怎么划分?

上一篇讲完 Generate 之后,一个 AUTOSAR 配置终于从 Configurator 进入了 C 代码。 于是打开工程目录,会突然看到大量文件: CanIf_Cfg.c CanIf_Cfg.h CanIf.cDem_Cfg.c Dem_Cfg.h Dem.cMcu.c Port.c Can.cRte_xxx.c Rte_xxx.hCtAp_xxx.c CtAp_xxx.h它们看起来都是: .c .…

作者头像 李华
网站建设 2026/9/24 19:06:30

152、MLIR的Dataflow(数据流)模型与静态调度

MLIR的Dataflow(数据流)模型与静态调度 从一次诡异的死锁说起 去年调一个AI加速器后端,跑一个简单的卷积+ReLU+池化流水线,结果在硬件仿真阶段卡死了。波形一看,某个PE(处理单元)的输入缓冲一直空着,但上游的DMA明明已经把数据写到了共享内存。查了三天,最后发现是M…

作者头像 李华
网站建设 2026/9/24 19:04:03

Python实现省际绿色全要素生产率测算:基于SBM-DDF模型的完整实操指南

做省际绿色全要素生产率(GTFP)测算,最近几年我在学术项目里几乎每周都要碰一次。单纯的TFP只考虑资本、劳动和产出,而GTFP还要把能源消耗和污染排放这类非期望产出拉进生产框架,这就得靠SBM-DDF模型来做前沿效率测算&a…

作者头像 李华