哎,说起这个Python怎么删除NaN,我这心里头就好像打翻了五味瓶,滋味儿复杂得很。你是不是也常常有这种感觉?辛辛苦苦从数据库里、文件里、网页上爬来一堆数据,打开一看,好家伙,密密麻麻的表格里,到处都是NaN,这个“Not a Number”的鬼东西,简直就是数据分析师的噩梦,横亘在你和“整洁数据”之间的一道天堑。每次看到那些NaN,我都觉得它们不是简简单单的缺失值,更像是一双双嘲讽的眼睛,在说:“嘿,想分析?没门儿!”

刚入行那会儿,我还是个愣头青,看见NaN就头大,恨不得直接把它们出现的行或者列一股脑儿全删了,图个眼不见心不烦。那时的我,哪里懂什么数据完整性、信息损失?只知道,只要模型能跑起来,报表能出来,任务就算完成了。结果呢?模型跑得那叫一个“欢快”,结果却是一塌糊涂,客户反馈回来一堆“这数据对不上啊”、“逻辑不通啊”。那时候才明白,这NaN,可不是什么善茬,它能悄无声息地毁掉你的心血,让你所有的努力都付诸东流。它就像一颗颗潜藏的地雷,一不小心,就能把你整个分析项目炸得粉碎。

NaN到底为啥这么“招人恨”?很简单啊,你想想,你在做数学运算,比如求平均值、标准差,甚至更复杂的回归分析、聚类算法,如果数据里夹杂着一堆NaN,很多函数根本就没法儿算,直接报错给你看。或者更阴险点儿,有些函数它不报错,它直接跳过NaN去算,结果你得到一个“看似合理”的数字,但实际上,那数字代表的样本量和统计意义早就被NaN给稀释、扭曲了。就好比你在做饭,结果锅里混进了几块石头,你还指望能做出美味佳肴?痴心妄想!所以,处理NaN,不仅仅是技术活儿,更是个良心活儿,它关乎着你分析结果的准确性,关乎着你最终决策的靠谱程度。

那么,到底该怎么着手,才能把这些可恶的NaN给“清理”干净呢?在Python里,我们最常用的利器,非Pandas莫属。它就像一个百宝箱,里面装满了各种对付NaN的“秘密武器”。

一、挥刀斩乱麻:dropna()的“暴力美学”

最直接、最粗暴的方式,就是删除。Pandas里的dropna()方法,就是干这个的。它能把含有NaN的行或者列,直接从你的DataFrame里剔除出去。

想想看,当你的数据集里,某个变量的缺失值实在太多了,多到你觉得它已经没有任何参考价值了,或者干脆就是某几行数据错得离谱,NaN只是表象,那么,dropna()就是你的不二选择。

“`python
import pandas as pd
import numpy as np

假设我有一个DataFrame,数据就像我那乱糟糟的桌面,啥都有

data = {
‘A’: [1, 2, np.nan, 4, 5],
‘B’: [np.nan, 2, 3, 4, 5],
‘C’: [1, 2, 3, np.nan, np.nan],
‘D’: [np.nan, np.nan, np.nan, np.nan, np.nan] # 这一列就是个废弃的深坑
}
df = pd.DataFrame(data)
print(“原始数据:\n”, df)

1. 默认删除:只要行里有NaN,整行就没了

df_cleaned_default = df.dropna()

print(“\n默认删除NaN行:\n”, df_cleaned_default)

这一招,看着干净,但代价是巨大的,可能一下子就把你的数据量干掉一大半。

我就曾经这么干过,结果发现原始数据2万行,处理完只剩几百行了,哭都没地方哭去。

2. how='all':只有当一行所有值都是NaN时才删

df_cleaned_how_all = df.dropna(how=’all’)
print(“\n只有所有值为NaN的行才删除:\n”, df_cleaned_how_all)

这种方式就温和多了,它会放过那些“局部缺失”的行,只针对那些“全军覆没”的行。

比如我上面那个例子,如果有一行全都是NaN,那它就会被干掉。

个人感觉,这才是dropna比较合理的打开方式,至少能保住大部分数据。

3. axis=1:删除含有NaN的列

df_cleaned_cols = df.dropna(axis=1)

print(“\n删除含有NaN的列:\n”, df_cleaned_cols)

比如我的’D’列,就是那种“一无是处”的列,干脆直接删了。

但是,如果你的列里只有几个NaN,你也要删掉整列吗?那代价也太大了。

所以,axis=1的时候,通常我会配合thresh或者事先检查缺失比例。

4. subset:只看特定列的NaN来决定删除与否

df_cleaned_subset = df.dropna(subset=[‘A’, ‘B’])
print(“\n只根据’A’和’B’列删除NaN行:\n”, df_cleaned_subset)

这一招我很喜欢用。比如,我有一个用户行为数据,如果“用户ID”和“行为时间”这两个核心字段是NaN,

那这条记录基本就是废的,删了也不可惜。这种“精准打击”既保证了核心数据的完整性,又避免了过度删除。

5. thresh:保留至少有N个非NaN值的行

df_cleaned_thresh = df.dropna(thresh=3) # 至少有3个非NaN值才保留
print(“\n至少有3个非NaN值的行才保留:\n”, df_cleaned_thresh)

这参数简直就是“设定最低生存标准”。它给了你很大的灵活性,

让你能控制删除的粒度。比如,我要求一行数据,至少有80%的有效信息,

那么我就可以根据总列数,设置一个合适的thresh值。

dropna()就像一把锋利的刀,用好了能帮你快刀斩乱麻,去芜存菁;用不好,那就是自损八百,伤敌为零,你的数据集很可能就被你“剁”得面目全非了。所以,用之前,一定要三思,要先用df.isnull().sum()看看各列的NaN数量,心里有个底。

二、精雕细琢:fillna()的“艺术修复”

dropna()固然简单粗暴,但多数情况下,我们是舍不得直接删除数据的。毕竟,数据就像金子,每一条都来之不易。这时候,fillna()就闪亮登场了。它不是删除,而是填充,用某种策略来替代那些NaN值,让你的数据“完整”起来。这才是真正考验你对数据理解深度和业务洞察力的环节。

fillna()的方法五花八门,每一种都有其适用的场景和内在逻辑。这不像dropna()那么一刀切,fillna()更像是一门艺术,需要你小心翼翼地雕琢。

1. 补丁大师:用常数或统计值填充

最直接的想法,就是用一个固定的值去填补。

“`python

df_filled_zero = df.fillna(0) # 简单粗暴,用0填充

print(“\n用0填充NaN:\n”, df_filled_zero)

这种方法慎用!比如“年龄”你填0,那可就太奇怪了。

但对于某些确实可以为0的数值,或者分类变量里的“未知”,倒是可以用。

比如,一个商品的“销量”,缺失了可能就是0。一个用户问卷的“未回答”,可以填’Unknown’。

用列的平均值、中位数或众数填充

假设’A’列代表某种数值型数据,比如收入

df_filled_mean = df.copy() # 复制一份,避免修改原始df
df_filled_mean[‘A’] = df_filled_mean[‘A’].fillna(df_filled_mean[‘A’].mean())
print(“\n用’A’列均值填充:\n”, df_filled_mean)

平均值适用于数据分布比较均匀的情况。

但如果你的数据里有异常值,平均值会被拉偏,这时候用中位数(median)就更稳健。

比如,df[‘A’].fillna(df[‘A’].median())

众数(mode)适用于分类数据或离散数据。

比如,df[‘C’].fillna(df[‘C’].mode()[0]) # mode()返回的是Series,可能包含多个众数,取第一个

我经常用它来填充一些分类特征,比如缺失的“省份”、“职业”等等。

“`

用统计值填充,就像给数据打了个“统计学上的补丁”。它能保证数据的整体分布不至于被大幅度破坏,但它毕竟只是个估计,不是真实值。你得结合业务场景,仔细评估这种填充方式是否合理。比如,填补年龄,用中位数往往比平均值更靠谱,因为年龄分布通常不是绝对正态的。

2. 前后左右看:ffill()bfill()的“邻里互助”

这两个方法,在处理时间序列数据时简直是神来之笔。它们会用缺失值前面(ffill,forward fill)或后面(bfill,backward fill)的有效值来填充。

“`python

假设我们有时间序列数据,比如股票价格或传感器读数

time_data = {
‘Time’: pd.to_datetime([‘2023-01-01’, ‘2023-01-02’, ‘2023-01-03’, ‘2023-01-04’, ‘2023-01-05’]),
‘Value’: [10, np.nan, 12, np.nan, 14]
}
df_time = pd.DataFrame(time_data).set_index(‘Time’)
print(“\n原始时间序列数据:\n”, df_time)

df_time_ffill = df_time.fillna(method=’ffill’)
print(“\n前向填充(ffill):\n”, df_time_ffill)

1月2日的NaN被1月1日的10填充了。1月4日的NaN被1月3日的12填充了。

这种方式假定缺失值与其前面的值有强关联,比如股价,今天的股价缺失了,用昨天的替代,勉强能接受。

df_time_bfill = df_time.fillna(method=’bfill’)
print(“\n后向填充(bfill):\n”, df_time_bfill)

1月2日的NaN被1月3日的12填充了。1月4日的NaN被1月5日的14填充了。

有时候,你可能觉得未来的趋势更重要,或者在数据收集时,某些信息是延迟到达的。

“`

这两种方法,好比是你在问路,前面那个人刚走过去,你问他到哪里,或者后面那个人正要来,你问他从哪里来。它假设数据在时间或序列上具有连续性。但要小心,如果你的缺失值太多,或者缺失的范围太广,这种填充方式可能会引入较大的偏差。

3. 化腐朽为神奇:interpolate()的“曲线救国”

如果说ffillbfill是线性地前后照搬,那么interpolate()就是真正的“曲线救国”,它会根据缺失值前后(甚至更远)的有效数据,通过各种插值算法,去估计那个缺失的值。这才是真正的“艺术活儿”,因为它试图捕捉数据的内在趋势。

“`python
df_interpolate = df.copy()
df_interpolate[‘A’] = df_interpolate[‘A’].interpolate(method=’linear’)
print(“\n用线性插值填充’A’列:\n”, df_interpolate)

比如,1和4之间有个NaN,线性插值会把它估计为2.5。

这种方法在处理数值型数据时,尤其当缺失值不多且分布相对均匀时,效果奇好。

它能让你的数据看起来更“自然”、更“平滑”。

还有更高级的插值方法:

df[‘Value’].interpolate(method=’polynomial’, order=2) # 二次多项式插值,适合有曲线趋势的数据

df[‘Value’].interpolate(method=’spline’, order=3) # 样条插值,更平滑

如果你的DataFrame索引是datetime类型,还可以用method=’time’,它会考虑时间的间隔。

“`

interpolate()是我最喜欢用的方法之一。它不像简单的均值填充那样粗糙,也不像dropna()那样简单粗暴地扔掉数据。它通过数学模型,试图“理解”数据的内在逻辑,然后“聪明地”补齐缺失的部分。用好了,你的数据会显得非常有生命力。当然,它的前提是,你的数据确实存在某种可插值的模式。

三、知己知彼:处理NaN前的“侦察工作”

在动手处理NaN之前,请务必、一定要先做“侦察工作”。就像打仗一样,你得知道敌人有多少、分布在哪里,才能制定合适的战略。

“`python

看看哪些列有NaN,有多少个?

print(“\n每列NaN数量:\n”, df.isnull().sum())

看看NaN的百分比,这更重要

print(“\n每列NaN百分比:\n”, df.isnull().sum() / len(df) * 100)

如果某一列NaN的百分比超过了80%甚至90%,那基本上这列就废了,直接删除可能更明智。

看看哪些行有NaN?

print(“\n含有NaN的行:\n”, df[df.isnull().any(axis=1)])

这能让你对缺失数据的分布有个直观感受。

“`

你得坐下来,盯着这些数字,结合你的业务知识去思考:
* 这个NaN出现,是因为数据采集出错?还是它本身就代表着“没有”或者“不适用”?
* 如果我用均值填充,会不会把一些极端值引入,扭曲我的统计结果?
* 如果我直接删除,我损失了多少数据量?这些被删除的数据,是否包含其他有用的信息?

这没有标准答案,每份数据、每个业务场景,都有其独特的“脾气”。处理NaN,更像是在做一道没有唯一正确答案的数学题。一半靠技术,一半靠经验,还有一半,可能就是靠那么点儿对数据的“直觉”和“敬畏”。

写在最后:这不仅仅是代码,更是决策

说到底,Python怎么删除NaN,不仅仅是敲几行代码那么简单。它背后是对数据的深刻理解,对业务场景的洞察,以及对未来分析结果负责的态度。你选择删除,还是选择填充,用哪种方式填充,每一次决定,都会对你最终的分析模型、预测结果产生深远影响。

我见过太多人,拿着一份NaN遍布的数据,随手dropna()或者fillna(0),然后就去跑模型,最后出来的结果自然是漏洞百出。也见过一些同事,为了处理好NaN,能够花上几天甚至几周的时间,反复尝试不同的方法,对比填充前后的数据分布,甚至咨询业务专家,最终,他们得到了真正有价值、有说服力的分析结果。

所以,朋友,当你下次再遇到那些让人头疼的NaN时,请停下来,深呼吸,别急着动手。先观察,再思考,然后才去选择最适合你数据的“武器”。记住,数据清洗,尤其是NaN的处理,是数据分析过程中最不起眼,却也最关键的一环。它决定了你后续所有工作的质量。把它处理好了,你的数据分析之路,才能走得更稳、更远。祝你和NaN的“战斗”,每战皆捷!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。