071、Pandas 入门:Series 与 DataFrame 的创建、选择、过滤基础

071、Pandas 入门:Series 与 DataFrame 的创建、选择、过滤基础

一个让我熬夜到凌晨3点的bug

上周帮同事排查一个数据清洗脚本,代码逻辑看着没问题,但输出结果总是少了几行。我盯着屏幕看了两个小时,咖啡喝了三杯,最后发现罪魁祸首是——他用列表索引的方式去取DataFrame的行,结果索引值不连续,直接跳过了好几条数据。这种坑,新手踩一次能记一辈子。

Pandas的Series和DataFrame,看着像Excel表格,用起来像Excel表格,但底层逻辑完全不是那么回事。今天我把这些基础操作掰开揉碎了讲清楚,顺便把我踩过的坑都标出来。

Series:一维数据,但别当列表用

创建Series的几种姿势

importpandasaspdimportnumpyasnp# 最基础的创建方式s1=pd.Series([1,3,5,np.nan,6,8])# 这里踩过坑:np.nan是浮点数,如果整列混入nan,整列会变成float64# 别这样写:pd.Series([1, 2, None]) # None会被自动转为NaN,类型变成object# 带索引的创建s2=pd.Series([10,20,30],index=['a','b','c'])# 索引可以是字符串、整数、甚至混合类型,但别混合,后面切片会疯掉# 从字典创建,键自动变成索引data_dict={'apple':3.5,'banana':2.0,'orange':4.0}s3=pd.Series(data_dict)# 这个用法在数据映射时特别爽,比如把商品名映射到价格

Series的选择操作——这里最容易翻车

s=pd.Series([10,20,30,40,50],index=['a','b','c','d','e'])# 位置索引(从0开始)print(s[0])# 10print(s[1:3])# 注意:位置切片是左闭右开,输出b和c# 标签索引(用index取值)print(s['a'])# 10print(s['a':'c'])# 这里坑来了:标签切片是闭区间!输出a,b,c# 别这样写:s[0]和s['a']混用,代码维护时自己都看不懂# 推荐写法:用.iloc和.loc明确意图print(s.iloc[0])# 明确用位置取print(s.loc['a':'c'])# 明确用标签取,闭区间# 这个习惯养成了,后面处理复杂索引时能少掉一半头发

过滤操作

s=pd.Series([15,22,8,30,12],index=['a','b','c','d','e'])# 布尔索引——最常用的过滤方式filter_condition=s>15print(s[filter_condition])# 输出b和d# 一行搞定print(s[s>15])# 多条件过滤print(s[(s>10)&(s<25)])# 注意括号不能省,&不能写成and# 这里踩过坑:用and会报错,因为Series的布尔运算必须用位运算符

DataFrame:二维表格,但别当Excel用

创建DataFrame的实战姿势

# 从字典列表创建——最常用的方式data={'name':['张三','李四','王五','赵六'],'age':[25,30,35,28],'salary':[8000,12000,15000,9500],'department':['技术部','市场部','技术部','财务部']}df=pd.DataFrame(data)# 列的顺序默认按字典插入顺序,想控制顺序就指定columns参数# 从嵌套字典创建nested_data={'张三':{'age':25,'salary':8000},'李四':{'age':30,'salary':12000}}df2=pd.DataFrame(nested_data).T# .T转置,让名字变成行索引# 这个技巧在处理JSON数据时特别有用# 从CSV读取(实际工作中90%的情况)# df = pd.read_csv('data.csv', encoding='utf-8')# 别这样写:不指定encoding,遇到中文直接乱码

列选择——最基础也最容易出错

# 选择单列——返回Seriesname_col=df['name']# 返回Series# 或者用点号name_col=df.name# 也能用,但列名有空格或特殊字符时炸裂# 选择多列——返回DataFramesubset=df[['name','salary']]# 注意是双层括号# 别这样写:df['name', 'salary'] # 这是元组索引,会报错# 选择行——用切片first_three=df[:3]# 前3行# 这里踩过坑:df[0:3]和df.iloc[0:3]效果一样,但df[0]和df.iloc[0]完全不同# df[0]会报错(列名是0时才有效),df.iloc[0]才是取第一行

行选择——.loc和.iloc的终极对决

# .loc:基于标签(行索引名和列名)# .iloc:基于位置(整数索引)# 选择单行print(df.loc[0])# 索引为0的行print(df.iloc[0])# 第一行(如果索引不是0,结果可能不同)# 选择多行print(df.loc[0:2])# 标签切片,闭区间,包含0,1,2print(df.iloc[0:2])# 位置切片,左闭右开,包含0,1# 行列同时选择print(df.loc[0:2,['name','salary']])# 前3行的name和salary列print(df.iloc[0:2,0:2])# 前2行的前2列# 实战技巧:用布尔索引选行再选列tech_dept=df[df['department']=='技术部']print(tech_dept[['name','salary']])# 这个组合拳能解决80%的数据筛选需求

过滤操作——从入门到精通

# 单条件过滤tech_staff=df[df['department']=='技术部']# 多条件过滤high_salary_tech=df[(df['department']=='技术部')&(df['salary']>10000)]# 注意:&两边要加括号,这是新手最容易犯的错误# 使用.isin()进行多值匹配tech_or_market=df[df['department'].isin(['技术部','市场部'])]# 字符串方法过滤name_contains_张=df[df['name'].str.contains('张')]# 这里踩过坑:直接用df['name'].contains('张')会报错,必须用.str访问器# 空值处理df_with_nan=df.copy()df_with_nan.loc[1,'salary']=np.nan valid_rows=df_with_nan[df_with_nan['salary'].notna()]# 别这样写:df_with_nan[df_with_nan['salary'] != np.nan] # NaN不等于任何值,包括自身

一个真实场景的完整案例

上周处理一份销售数据,需求是找出"技术部中工资高于平均水平的员工":

# 模拟数据sales_data={'name':['张三','李四','王五','赵六','钱七'],'dept':['技术部','市场部','技术部','财务部','技术部'],'salary':[8000,12000,15000,9500,11000]}df=pd.DataFrame(sales_data)# 第一步:筛选技术部tech_df=df[df['dept']=='技术部']# 第二步:计算技术部平均工资avg_salary=tech_df['salary'].mean()# 第三步:筛选高于平均工资的result=tech_df[tech_df['salary']>avg_salary]print(result)# 输出:王五和钱七

这个案例看起来简单,但实际工作中数据量大了之后,性能问题就出来了。如果数据有10万行,建议用.query()方法:

result=df.query("dept == '技术部' and salary > salary.mean()")# 一行搞定,而且底层用numexpr加速,比布尔索引快30%左右

个人经验总结

  1. 永远用.iloc和.loc:别偷懒用df[0]这种写法,三个月后你自己都看不懂。明确告诉读者你是按位置还是按标签取数据。

  2. 链式操作要谨慎df[df['a']>0]['b']这种写法会触发SettingWithCopyWarning,正确做法是df.loc[df['a']>0, 'b']

  3. 数据类型是隐形杀手:创建Series时混入None或np.nan,整列类型会变。用df.dtypes随时检查,用pd.to_numeric()强制转换。

  4. 索引重置是基本功:过滤后的DataFrame索引可能不连续,用reset_index(drop=True)重置,否则后面用.loc取行时会踩坑。

  5. 调试时多用.shape和.head():每步操作后看一眼数据形状,能快速定位问题。我见过太多人写了50行代码才发现第一步就错了。

Pandas的学习曲线确实陡峭,但掌握了Series和DataFrame的创建、选择、过滤这三个基础操作,后面学groupby、merge、pivot_table就会顺畅很多。记住:数据操作的核心就三件事——选行、选列、条件过滤,所有复杂操作都是这三者的组合。