071、Pandas 入门:Series 与 DataFrame 的创建、选择、过滤基础
一个让我熬夜到凌晨3点的bug
上周帮同事排查一个数据清洗脚本,代码逻辑看着没问题,但输出结果总是少了几行。我盯着屏幕看了两个小时,咖啡喝了三杯,最后发现罪魁祸首是——他用列表索引的方式去取DataFrame的行,结果索引值不连续,直接跳过了好几条数据。这种坑,新手踩一次能记一辈子。
Pandas的Series和DataFrame,看着像Excel表格,用起来像Excel表格,但底层逻辑完全不是那么回事。今天我把这些基础操作掰开揉碎了讲清楚,顺便把我踩过的坑都标出来。
Series:一维数据,但别当列表用
创建Series的几种姿势
import pandas as pd
import numpy as np
# 最基础的创建方式
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]) # 10
print(s[1:3]) # 注意:位置切片是左闭右开,输出b和c
# 标签索引(用index取值)
print(s['a']) # 10
print(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 > 15
print(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,遇到中文直接乱码
列选择——最基础也最容易出错
# 选择单列——返回Series
name_col = df['name'] # 返回Series
# 或者用点号
name_col = df.name # 也能用,但列名有空格或特殊字符时炸裂
# 选择多列——返回DataFrame
subset = 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,2
print(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%左右
个人经验总结
-
永远用.iloc和.loc:别偷懒用df[0]这种写法,三个月后你自己都看不懂。明确告诉读者你是按位置还是按标签取数据。
-
链式操作要谨慎:
df[df['a']>0]['b']这种写法会触发SettingWithCopyWarning,正确做法是df.loc[df['a']>0, 'b']。 -
数据类型是隐形杀手:创建Series时混入None或np.nan,整列类型会变。用
df.dtypes随时检查,用pd.to_numeric()强制转换。 -
索引重置是基本功:过滤后的DataFrame索引可能不连续,用
reset_index(drop=True)重置,否则后面用.loc取行时会踩坑。 -
调试时多用.shape和.head():每步操作后看一眼数据形状,能快速定位问题。我见过太多人写了50行代码才发现第一步就错了。
Pandas的学习曲线确实陡峭,但掌握了Series和DataFrame的创建、选择、过滤这三个基础操作,后面学groupby、merge、pivot_table就会顺畅很多。记住:数据操作的核心就三件事——选行、选列、条件过滤,所有复杂操作都是这三者的组合。

359

被折叠的 条评论
为什么被折叠?



