资讯详情

资讯详情

Pandas Series 常用运算详解:从算术对齐到缺失值处理

1. 从数据结构定位说起为什么Series运算值得单独开一篇做数据分析的朋友都知道Pandas里有两个最主要的容器DataFrame和Series。DataFrame看起来更像一张表Series看起来就是一列数据加上一个行标签。很多初学者习惯把精力都放在DataFrame上觉得Series太简单、不值得花时间真正开始做数据处理后就会发现DataFrame里几乎所有列级别的操作本质上都是把某一列取出来当作Series再对Series做运算。你掌握得越扎实后面写复杂聚合、写清洗逻辑的时候就越顺手。这一篇我们聚焦Series的“常用运算”不聊创建、索引这些基础前面的文章已经完整覆盖过就聊拿一个Series在手之后怎么把它用活。包括算术运算、比较运算、统计运算、布尔筛选、去重排序、缺失值处理以及这些操作在实际项目中常见的坑。我把每个知识点都配合代码演示和踩坑记录写清楚方便你直接照着练也能用在真实的分析任务里。先提醒一句如果你的Pandas版本还停留在1.x部分输出格式略有差异但不影响逻辑。我本地环境是Python 3.10 Pandas 2.0.3读这篇文章的时候建议至少Pandas 1.5以上尽量贴近新版本因为很多行为优化是结合新版本调整的。import numpy as np import pandas as pd pd.set_option(display.max_columns, 50) pd.set_option(display.width, 150)2. 算术运算与广播机制用好“对齐”才能少掉头发2.1 Series与标量运算一个循环都别写先说说最简单也最高频的场景对整个Series做统一变换。比如订单金额从元换算成万元、价格乘以折扣系数、对百分比字段做归一化这些操作直接用运算符就行。price pd.Series([199, 299, 159, 399, 249], nameprice) print(price * 0.85)0 169.15 1 254.15 2 135.15 3 339.15 4 211.65 Name: price, dtype: float64这个结果背后核心机制是广播Pandas会自动把标量0.85扩展到每个元素上。整个过程是向量化的底层走的是NumPy的循环比Python原生写法高效得多。同样道理加、减、乘、除、取余、幂运算都可以直接用s pd.Series([10, 20, 30]) print(s 1) print(s - 2) print(s / 4) print(s // 7) print(s ** 2)0 11 1 21 2 31 dtype: int64 0 -1 1 18 2 28 dtype: int64 0 2.5 1 5.0 2 7.5 dtype: float64 0 1 1 2 2 4 dtype: int64 0 100 1 400 2 900 dtype: int64有个细节值得注意整数Series做除法时会自动转成浮点数因为结果是小数Pandas不会强行取整。但是取整除法和取余则会保持整数类型这一点在做金额分账、分页计算的时候要提前想清楚避免类型隐式转换引发的误判。这里还有一个小坑就是Series为整型时如果标量是负数取模运算结果和Python原生行为一致但和C、Java等语言不同。遇到混用的情况先确认符号是否符合预期。2.2 Series与Series运算索引对齐才是精髓两个Series做运算最核心的机制是索引对齐。也就是Pandas会把标签相同的元素配对计算标签不同的位置结果为NaN。这个特性非常强大也特别容易踩坑。s1 pd.Series([1, 2, 3], index[a, b, c]) s2 pd.Series([10, 20, 30], index[b, c, d]) print(s1 s2)a NaN b 22.0 c 33.0 d NaN dtype: float64s1里有a、b、cs2里有b、c、d最终结果是四个索引的并集。a和d只有一个系列有值结果就是NaN。这里面的b是12022c是33033都没问题。很多人第一次遇到这种情况会以为数据算错了其实这正是Pandas的设计核心之一。真实项目中经常遇到两个不同来源的数据需要按日期、按用户、按品类对齐这时候直接用加号比写循环匹配省太多事。但反过来说如果没意识到索引对齐就会在自己没注意的时候把数据算成NaN。尤其两个Series的索引顺序不一样但元素数量一样时Pandas仍然是按标签匹配而不是按位置匹配。这是个高频陷阱。left pd.Series([1, 2, 3], index[x, y, z]) right pd.Series([4, 5, 6], index[z, y, x]) print(left right)x 7 y 7 z 7 dtype: int64这里索引标签虽然顺序不同但因为每个标签都能对上所以结果全部有值。如果内心默认“按顺序加”一旦遇到索引顺序不一致的数据就会出错而且这种错误往往很隐蔽肉眼不容易察觉。2.3 与NumPy数组、列表运算位置对齐规则要分清Series与列表或NumPy数组运算时不按索引对齐而是按位置对齐。也就是说Pandas会忽略Series的索引标签直接把序列值和传入的序列逐个位置相加减。如果长度不一致会直接报错。s pd.Series([1, 2, 3], index[a, b, c]) arr np.array([10, 20, 30]) print(s arr)a 11 b 22 c 33 dtype: int64再对比一下arr_reversed np.array([30, 20, 10]) print(s arr_reversed)a 31 b 22 c 13 dtype: int64看出区别了吗传入NumPy数组时标签a对应的1加的是30而不是10说明是位置对齐。这在实际项目中很常见你从数据库取到一列数据又拿到一份外部计算好的数组两者含义是一一对应的但顺序可能一致也可能不一致结果就会天差地别。所以用数组做运算前务必确认顺序确定有序或者直接用索引对齐的方式合并成Series再算。2.4 缺失值一旦出现算术结果全变NaN当Series本身含有NaN任何算术运算都会让结果变成NaN这就涉及“NaN传染”。s pd.Series([1, np.nan, 3, 4]) print(s * 2)0 2.0 1 NaN 2 6.0 3 8.0 dtype: float64这符合数据分析直觉缺失值无法参与计算。包括加法、乘法、幂运算一概如此。在数据清洗阶段这是最折磨人的部分。你以为数据都填补干净了结果一跑统计全是NaN多半就是某列还有残留缺失值。所以做任何运算前先检查isna().sum()和dropna()这个习惯必须养成。另外Series加Series时如果一边缺失结果就是NaN不会自动把非缺失那一侧的值保留下来。想实现“有值用值、没值用另一边”的效果得用combine_first或fillna后面会专门讲。3. 缺失值处理没有NaN意识统计结果全白搭3.1 缺失值的来源别等结果异常了才回头查缺失值最常见来源有四种一是原始数据本身就是空的数据库里存的是NULL导入Pandas后就变成NaN二是索引对齐时两端标签不一致补出来的位置是NaN三是左连接、外连接这种方式合并数据表之后NaN是一种常态四是我们自己用赋值的方式指定的缺失标记比如把某些异常值替换成np.nan后面统一处理。s pd.Series([100, 200, None, 400]) print(s)0 100.0 1 200.0 2 NaN 3 400.0 dtype: float64Python自带的None传入Series后Pandas自动识别并转成NaN输出时展示为NaN。这一行为在Pandas 2.0里更加严格建议日常处理中尽量统一使用np.nan或pd.NA避免None和NaN混用导致判断异常。3.2 判断缺失值isna还是isnull别选错Pandas提供了两组方法isna/isnull、notna/notnull。它们的作用完全一样isna与isnull是别名关系。个人习惯用isna写起来短而且和DataFrame的interpolate、fillna这些方法命名风格一致语义更清晰。s pd.Series([1, np.nan, 3]) print(s.isna())0 False 1 True 2 False dtype: bool布尔结果的Series最常用在两个场景一是统计缺失个数直接sum()即可二是作为布尔索引只保留非缺失或只保留缺失的部分。data pd.Series([10, np.nan, 30, np.nan, 50]) valid data.notna() print(data[valid])0 10.0 2 30.0 4 50.0 dtype: float64这比在循环里判断再过滤元素快得多也更符合Pandas的向量化思维。3.3 缺失值剔除与填充两个策略三条路处理缺失值一般两条路要么删掉要么填上。删掉直接用dropna()默认是丢掉所有含NaN的行s pd.Series([1, np.nan, 3, np.nan, 5]) print(s.dropna())0 1.0 2 3.0 4 5.0 dtype: float64dropna返回的是一个新的Series不会原地修改原数据。如果你希望直接改原对象用inplaceTrue或者赋回去。实测中inplaceTrue在很多Pandas新版本里已经被标记为即将弃用建议直接用赋值方式。填充用fillna(value)可以把所有缺失值替换成固定值。最常见的几种场景s pd.Series([1, np.nan, 3, np.nan, 5]) print(s.fillna(0)) print(s.fillna(methodffill)) print(s.fillna(methodbfill))0 1.0 1 0.0 2 3.0 3 0.0 4 5.0 dtype: float64 0 1.0 1 1.0 2 3.0 3 3.0 4 5.0 dtype: float64 0 1.0 1 3.0 2 3.0 3 5.0 4 5.0 dtype: float64ffill表示用前一个有效值填充bfill表示用后一个有效值填充。fillna(methodffill)在Pandas 2.0中已经被调整为使用fillna(method...)但在某些版本会有告警更推荐的方式是print(s.ffill()) print(s.bfill())ffill和bfill这两个方法在时间序列处理里特别有用。比如传感器数据每隔几秒记录一次中间偶尔丢包用ffill把上一个观测值延续到缺失位置是最自然的填补策略。但注意一点如果缺失值出现在最前面ffill无法填补因为前面没有有效值bfill同理无法填补最后面的缺失值。这时可以组合使用print(s.ffill().bfill())先向前填充再向后填充这样首尾缺失都能处理但也要结合实际场景确认这样做是否合理。3.4 还有一条路插值法比固定值和前后填充更精细的是插值法。Series.interpolate()默认使用线性插值它根据缺失值前后两个有效值的坐标计算中间等距位置的估计值。s pd.Series([1, np.nan, np.nan, 4]) print(s.interpolate())0 1.0 1 2.0 2 3.0 3 4.0 dtype: float64这里两个NaN被线性插值为2.0和3.0刚好落在1到4的等分点上。插值法适合数据本身有连续变化趋势的场景比如温度曲线、股票价格序列、网页访问量的时间序列。如果数据是离散分类变量插值就不合适了。还有一点提醒插值默认按索引数值大小进行。如果索引是字符串或非数值类型部分插值方法会失效使用前先检查索引类型。4. 比较运算与布尔索引几乎所有筛选逻辑都离不开它4.1 比较运算的结果是什么Series与标量做比较返回的是一个布尔Series每个位置是True或False。这个布尔Series几乎总是用来做下一步筛选的。score pd.Series([88, 92, 75, 60, 45], index[A, B, C, D, E]) print(score 60)A True B True C True D False E False dtype: bool很多人以为score 60会像普通数值运算一样返回一个新Series的分数结果其实不是它返回的是布尔掩码。要拿到筛选后的具体分数还需要用这个掩码去索引原Seriesprint(score[score 60])A 88 B 92 C 75 dtype: int64这种“布尔掩码”的思维是Pandas的核心用法跟NumPy完全一致。多条件组合时必须用与、|或、~非连接不能用Python的and、or、not因为后者只能用于标量布尔值不能用于数组或Series。print(score[(score 60) (score 90)]) print(score[(score 60) | (score 90)])A 88 C 75 dtype: int64 B 92 E 45 dtype: int64注意每个条件都要用括号包起来因为Pandas的运算符优先级跟普通算术不同忘记加括号会直接报错或给出奇怪的结果。4.2 isin与between两个高频替代手工条件的方法有时候筛选条件不是“大于某个值”或“小于某个值”而是“是否属于某个集合”。比如筛选指定几个城市的数据、指定几个商品类别的数据用isin就非常简单。city pd.Series([北京, 上海, 广州, 深圳, 杭州]) target [北京, 杭州, 成都] print(city.isin(target))0 True 1 False 2 False 3 False 4 True dtype: bool结合布尔索引直接取出目标城市print(city[city.isin(target)])0 北京 4 杭州 dtype: objectbetween方法用于判断是否在某个区间内闭区间两端都包含。这个方法比写上两个比较条件再加更简洁。age pd.Series([18, 25, 33, 42, 56]) print(age.between(20, 40))0 False 1 True 2 True 3 False 4 False dtype: boolbetween也支持inclusive参数用来控制边界是否包含print(age.between(20, 40, inclusiveleft))inclusive取值有both、left、right、neither默认是both即两端都包含。想开区间时用neither左开右闭用right非常灵活。4.3 any与all批量判断的快捷方式当得到一个布尔Series后有时我们不需要逐行处理只需要判断整体上是否存在满足条件的数据或者是否全部满足。这时候用any和allscore pd.Series([88, 92, 75, 60, 45]) print(score.gt(100).any()) # 是否有大于100的分数 print(score.gt(40).all()) # 是否所有分数都大于40False True在数据质量检查场景中非常有用。比如检查某一列是否有负数、是否有空字符串、是否所有值都在合理区间内一次any/all调用就出结论不需要写循环。4.4 布尔索引配合赋值定向修改数值布尔索引不止用于筛选展示还能直接配合赋值实现定向修改。比如把低于60分的成绩改成60分score[score 60] 60 print(score)0 88 1 92 2 75 3 60 4 60 dtype: int64这个操作在数据清洗里很常用比如把异常温度、异常价格直接设置成NaN或者把超过某一阈值的值截断。但这里有一个高频警告如果你对DataFrame的列做类似操作时触发的是链式赋值可能会遇到SettingWithCopyWarning。虽然那个警告在DataFrame里更常见但Series如果来自筛选出来的子视图也可能遇到类似问题。建议做赋值之前先确认当前Series是原对象的视图还是副本。最稳妥的办法是先用copy()显式复制score_copy score.copy() score_copy[score_copy 60] 60虽然会多占一份内存但对于数据量不大、分析任务追求稳定的场景完全可接受。后面我会单独讲这个坑的成因和排查方式。5. 统计运算与聚合一个函数摸清数据全貌5.1 描述性统计速查表Series里内置了大量统计方法用法统一都是返回标量。我把最常用的整理成一张表方法功能备注sum()求和mean()平均值median()中位数抗离群值能力强min() / max()最小值 / 最大值std()样本标准差分母为n-1var()样本方差分母为n-1count()非缺失值个数不含NaNskew()偏度判断分布对称性kurt()峰度判断分布尖峭程度quantile(q)分位数q为0~1之间的小数cumsum()累积和返回Seriescummax()累积最大值返回Seriesdescribe()综合统计描述返回Series实操中几个细节要提一下。第一mean、sum、std这些方法默认会跳过NaN这是Pandas的默认行为。也就是说直接调用s.mean()并不会因为存在NaN就返回NaN。这一点和前面的算术运算不同很多人一时没想明白。s pd.Series([1, 2, np.nan, 4, 5]) print(s.mean())3.0结果是3.0也就是(1245)/4。如果想遇到NaN就返回NaN可以加上skipnaFalseprint(s.mean(skipnaFalse))nan第二std和var的计算默认用样本公式分母是n-1而不是总体公式的n。如果你要算的是总体标准差要自己手动处理或用相应参数不同业务场景对“标准差”的定义并不一样金融风控和一些质量统计会更严格务必先确认口径。5.2 describe不止count、mean、std这几个指标describe()能一键输出多个统计量适合快速看数据分布。对于数值型Series默认输出count、mean、std、min、25%、50%、75%、max。price pd.Series([199, 299, 159, 399, 249]) print(price.describe())count 5.000000 mean 261.000000 std 90.013888 min 159.000000 25% 199.000000 50% 249.000000 75% 299.000000 max 399.000000 dtype: float64注意25%、50%、75%这几个分位数默认使用线性插值算法。如果你需要更精确的自定义分位点可以用quantile方法print(price.quantile([0.1, 0.9]))0.1 167.0 0.9 379.0 dtype: float64这里0.1分位数是167.00.9分位数是379.0说明大部分价格集中在中低区间高分位点拉高了整体均值。这种信息在定价策略和异常点识别中非常有用。5.3 累计运算的常见用法cumsum是累计和也就是每个位置的值等于当前元素及之前所有元素的和。这在分析销售额累计、播放量累计、里程累计的时候非常常用。sales pd.Series([100, 200, 150, 300]) print(sales.cumsum())0 100 1 300 2 450 3 750 dtype: int64cummax和cummin则常用来判断当前值是否突破了历史最高或最低点比如股票价格创出阶段新高high pd.Series([10, 12, 11, 15, 14]) print(high.cummax())0 10 1 12 2 12 3 15 4 15 dtype: int64另外cumprod是累积乘积在计算复利增长、环比连乘的场景中会用到但要注意溢出风险和数据量大小。5.4 与NumPy通用函数的配合有些统计逻辑Series没有内置方法但可以用NumPy的通用函数来做。比如np.sqrt对每个元素开方、np.log取对数、np.abs取绝对值、np.round四舍五入。s pd.Series([1, 4, 9, 16]) print(np.sqrt(s))0 1.0 1 2.0 2 3.0 3 4.0 dtype: float64这也是Pandas与NumPy无缝衔接的体现。对数值型Series做np.log时如果元素包含0或负数会产生-inf或NaN要提前考虑是否需要偏移比如log1p即log(x1)是处理非负偏态数据的常用手段。6. 去重、排序、排名与值频次统计清洗与观察的一把梭6.1 unique、nunique与drop_duplicates三个去重别混用unique()返回的是去重后的数组类型是NumPy ndarray不是Series。s pd.Series([苹果, 香蕉, 苹果, 橙子, 香蕉, 苹果]) print(s.unique())[苹果 香蕉 橙子]nunique()返回去重后的元素个数是标量整数。特别适合快速查看分类变量有多少个类别。print(s.nunique())3drop_duplicates()返回的是去掉重复行之后的Series保留的是每个重复值第一次出现的位置。print(s.drop_duplicates())0 苹果 1 香蕉 3 橙子 dtype: object三者应用场景不同想列出去重后的所有取值用unique只想知道有几个类别用nunique想在保留原Series完整索引体系的前提下过滤重复项用drop_duplicates。6.2 value_counts分类数据的黄金方法value_counts()是分类变量统计中最高频的方法。它统计每个值出现的次数默认按次数降序排列。city pd.Series([北京, 上海, 北京, 广州, 北京, 上海]) print(city.value_counts())北京 3 上海 2 广州 1 dtype: int64实际工作中常用normalizeTrue把频次转换成占比print(city.value_counts(normalizeTrue))北京 0.500000 上海 0.333333 广州 0.166667 dtype: float64这样直接得出“北京占比50%”等结论尤其适合做品类占比、渠道占比、用户分层占比等分析。value_counts还能按区间统计比如把连续年龄切成年龄段。先用pd.cut把连续值离散化再调用value_counts。ages pd.Series([23, 45, 31, 28, 52, 36, 41, 29]) bins pd.cut(ages, bins[0, 30, 40, 60]) print(bins.value_counts())(0, 30] 4 (30, 40] 2 (40, 60] 2 dtype: int64这个方法在分析用户年龄分布、收入分层、时长分桶时很实用。6.3 sort_values与sort_index别忽略排序的稳定性sort_values按值排序sort_index按索引排序。默认都是升序。s pd.Series([88, 92, 75, 60, 45], index[D, A, E, B, C]) print(s.sort_values()) print(s.sort_index())B 60 C 45 A 92 D 88 E 75 dtype: int64 A 92 B 60 C 45 D 88 E 75 dtype: int64下面说一个真实项目里容易忽略的点sort_values默认使用快速排序算法quicksort它不是稳定排序。这意味着如果Series里有多个相同的值排序后它们的原始相对顺序可能被改变。如果你需要保持相同值的先后关系可以指定kindmergesort。s pd.Series([1, 3, 2, 3, 1]) print(s.sort_values(kindmergesort))0 1 4 1 2 2 1 3 3 3 dtype: int64可见两个1的原始位置0和4、两个3的原始位置1和3在mergesort下都保持了原有顺序。这个细节在日常排序里用得不多但在某些需要可复现排序结果的场景下值得注意。6.4 rank方法并列时的排名策略rank()用于计算排名它和sort_values不同sort_values是排序rank是给每个元素一个名次。score pd.Series([90, 85, 90, 76, 88]) print(score.rank())0 4.5 1 2.0 2 4.5 3 1.0 4 3.0 dtype: float64这里的默认方法是average也就是两个90分并列第4和第5名平均后都取4.5。如果希望并列时都取最低名次用methodmin都取最高名次用methodmax按出现顺序给名次用methodfirst。print(score.rank(methodmin)) print(score.rank(methodmax)) print(score.rank(methodfirst))0 4.0 1 2.0 2 4.0 3 1.0 4 3.0 dtype: float64 0 5.0 1 2.0 2 5.0 3 1.0 4 3.0 dtype: int64 0 4.0 1 2.0 2 5.0 3 1.0 4 3.0 dtype: float64做竞赛排名、业绩排行、分数等级划分时选对排名策略很重要。默认的average会让人疑惑“排名怎么会有小数”其实这是Pandas为了让并列名次公平而设计的处理方式。6.5 idxmax与idxmin取极值对应的索引除了知道最大、最小值是多少很多时候更需要知道“最大/最小值发生在哪一行”。比如找出销量最高的月份、温度最高的日期、价格最低的商品。monthly_sales pd.Series( [120, 150, 110, 180, 160], index[1月, 2月, 3月, 4月, 5月] ) print(monthly_sales.idxmax()) print(monthly_sales.idxmin())4月 3月这让结论更有业务意义不只是“最高销量是180”而是“最高销量出现在4月达到180”。在项目汇报里这个信息比单纯数字直观得多。需要注意如果Series中存在多个相同的最大值idxmax默认返回第一个最高值所在的位置。这一点和nlargest(1)行为不同后者会返回排序后的结果而idxmax只返回索引标签。nlargest和nsmallest也是高频方法它们不排序整个Series而是直接取出最大的前n个或最小的前n个。print(monthly_sales.nlargest(2)) print(monthly_sales.nsmallest(2))4月 180 5月 160 dtype: int64 3月 110 1月 120 dtype: int64数据量很大时nlargest比sort_values().head(n)效率更高因为底层用了堆排序不需要把全部数据完整排序。7. 常见问题与排查技巧实录7.1 索引不匹配导致结果全是NaN这是我见过新手最容易踩的坑也是最难排查的一类问题。两个Series明明数值看起来差不多一相加结果却全是NaN第一反应往往是数据类型有问题打开数据一看又觉得没事最后才意识到是索引没对齐。s1 pd.Series([100, 200, 300], index[0, 1, 2]) s2 pd.Series([400, 500, 600], index[1, 2, 3]) print(s1 s2)0 NaN 1 600.0 2 800.0 3 NaN dtype: float64为什么会这样因为Pandas的算术运算按索引标签配对0和3这两个标签只在一边存在所以结果NaN。解决办法是先对齐索引再填充缺失值或者使用add方法的参数print(s1.add(s2, fill_value0))0 100.0 1 600.0 2 800.0 3 600.0 dtype: float64这里fill_value0告诉Pandas遇到缺失的索引时按0参与运算两个缺失的位置都补齐了。但要注意这种处理是否合理完全取决于业务语义如果缺失代表“没有数据”用0填充是合适的如果缺失是“采集异常”填充0会歪曲结果。7.2 布尔索引的优先级问题and与的混乱我见过很多人在多个条件筛选时报错报错信息五花八门ValueError、TypeError都有最终原因大多是混用了Python原生的and与Pandas的。s pd.Series([10, 20, 30, 40]) # 错误写法 # s[(s 10) and (s 40)] # ValueError # 正确写法 print(s[(s 10) (s 40)])1 20 2 30 dtype: int64这里有两个教训第一对Series做元素级逻辑运算必须用、|、~第二每个子条件都必须加括号因为的优先级高于比较运算符不加括号会被解释成先做位运算再做比较结果完全错误。7.3 dtype为object导致运算异常当一个Series的dtype是object时其实就是Python对象的容器。它可能是字符串、混合类型或者包含None。对它做数学运算时结果往往不符合预期。s pd.Series([10, 20, 30]) # s 5 会报错 TypeError只能将str不是int连接到str try: print(s 5) except TypeError as e: print(fTypeError: {e})解决办法是先转成数值类型s_numeric pd.to_numeric(s) print(s_numeric 5)0 15 1 25 2 35 dtype: int64pd.to_numeric还可以加errorscoerce参数把无法转换的值直接变成NaN这在清洗脏数据时特别有用。比如一列“金额”字段里混入了字符待定、N/A直接to_numeric会报错加coerce后自动变成NaN再配合fillna处理。7.4 inplace的坑赋值不生效Pandas里很多方法默认返回新对象不修改原对象。新手常犯的错误是直接调用s.sort_values()却不接收返回值导致排序“没生效”。s pd.Series([3, 1, 2]) s.sort_values() print(s)0 3 1 1 2 2 dtype: int64从输出看s没有变化因为sort_values返回的是新对象原s还是原来的顺序。正确的做法有两种s_sorted s.sort_values() print(s_sorted)或s.sort_values(inplaceTrue) print(s)在新版本Pandas中inplaceTrue虽然还能用但官方更推荐返回新对象的写法。我个人建议一律使用第一种因为链式调用更清晰也不会因为误用inplace导致视图副本问题。7.5 设置Pandas显示选项避免输出被截断处理较长的Series时Pandas默认只显示前后部分中间用省略号代替。这在调试时很烦人。可以用pd.set_option调整pd.set_option(display.max_rows, 100) pd.set_option(display.max_columns, 50) pd.set_option(display.width, 200)这几个选项分别控制最大行数、最大列数、整体显示宽度。数据分析实战里调大这些参数能减少很多不必要的疑惑。8. 问题速查表一边写代码一边查现象可能原因解决方案两个Series相加结果出现大量NaN索引没有对齐用add(fill_value0)或先对齐索引s 60结果不是数字而是True/False比较运算返回布尔Series用这个布尔Series做索引筛选多个条件用and连接报错Python and不能用于Series改用并给每个条件加括号series.unique()返回的不是Seriesunique返回ndarray用pd.Series(s.unique())转换value_counts结果顺序不对默认按频次降序用sort_index或sort_values调整字符串列做加法报错dtype是object用pd.to_numeric(..., errorscoerce)fillna后原数据没变方法默认返回新对象赋值给新变量或使用inplacedescribe没显示某些统计量列可能是object类型先to_numeric或astype(float)索引是日期类型但运算报错类型不匹配用pd.to_datetime统一转换sort_values后索引乱序排序后索引跟着值移动用reset_index(dropTrue)重建索引布尔索引赋值出现警告链式赋值导致视图副本问题先用copy()再赋值包含NaN的Series求sum结果不是NaN统计方法默认跳过NaN用skipnaFalse或先检查缺失这张表是我实际排查时最常用的索引遇到问题优先对照一下能省不少时间。最后分享一个我自己的习惯拿到一个陌生Series时先跑一遍dtype、isna().sum()、describe()、head()这四个操作能覆盖80%的数据质量判断。然后才是具体的业务运算。顺序对了后面少踩很多坑。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →