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

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%左右

个人经验总结

  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就会顺畅很多。记住:数据操作的核心就三件事——选行、选列、条件过滤,所有复杂操作都是这三者的组合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值