你有没有遇到过这种情况:
手里有一份10000行的销售数据,你想计算每个地区的平均销售额,结果用了3个for循环跑了5分钟,老板在旁边盯着进度条,尴尬得脚趾抠出了三室一厅。
或者更糟,你试图用Python原生列表处理数据,写了50行嵌套循环,最后发现数据类型不一致,程序崩溃了。
然后你百度了一圈,Stack Overflow翻了三页,最后发现——原来Pandas的DataFrame一行代码就能搞定。
别哭了,今天咱们就来彻底搞懂Pandas的两大核心:Series和DataFrame。看完这篇,下次再遇到数据分析任务,你就能优雅地说一句:"这玩意儿,我5分钟搞定。"
什么是Series?别被名字吓到了
说白了,Series就是一个带标签的一维数组。
想象一下你在超市购物,有一张购物清单:
商品A: 25元
商品B: 18元
商品C: 42元
这张清单就是Series:
- 商品A、B、C叫做索引(index)
- 25、18、42叫做值(values)
- 整个东西就是Series
import pandas as pd
# 创建一个Series
prices = pd.Series([25, 18, 42], index=['商品A', '商品B', '商品C'])
print(prices)
# 输出:
# 商品A 25
# 商品B 18
# 商品C 42
# dtype: int64
Series的核心价值:
- 比普通列表多了索引,可以通过标签快速查找
- 比字典多了数学运算能力
- 内存效率高,处理速度飞快
那DataFrame又是什么?
如果说Series是一张购物清单,那DataFrame就是整张购物发票。
import pandas as pd
# 创建DataFrame
data = {
'商品': ['苹果', '香蕉', '橙子'],
'价格': [25, 18, 42],
'数量': [5, 8, 3],
'总价': [125, 144, 126]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# 商品 价格 数量 总价
# 0 苹果 25 5 125
# 1 香蕉 18 8 144
# 2 橙子 42 3 126
看到没?DataFrame就是多个Series组成的表格:
- 每一列都是一个Series
- 每个Series都有相同的索引
- 整个东西构成了二维数据结构
为什么要搞两个东西?这不是多此一举吗?
当初我也想:搞这么复杂干嘛?一个结构不就够了吗?
直到我踩了坑才明白:
场景1:只需要单一维度数据时,Series更合适
# ❌ 用DataFrame处理单一数据,杀鸡用牛刀
temperatures = pd.DataFrame({
'temperature': [23, 25, 19, 30, 28]
})
# ✅ 用Series处理,简洁明了
temperatures = pd.Series([23, 25, 19, 30, 28],
index=['周一', '周二', '周三', '周四', '周五'])
场景2:需要复杂数据操作时,DataFrame才能救你
# 复杂数据分析
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 15000, 10000],
'部门': ['技术', '销售', '技术', '市场']
})
# 一行代码计算每个部门的平均工资
avg_salary = df.groupby('部门')['工资'].mean()
print(avg_salary)
# 输出:
# 部门
# 市场 10000
# 技术 11500
# 销售 12000
# Name: 工资, dtype: int64
这操作用纯Python写至少要20行代码,DataFrame一行搞定!
核心操作:这些你必须会
Series常用操作
# 创建Series
scores = pd.Series([85, 92, 78, 95], index=['语文', '数学', '英语', '理综'])
# 通过索引访问
print(scores['数学']) # 92
# 通过位置访问
print(scores[1]) # 92
# 切片
print(scores[1:3]) # 数学92, 英语78
# 数学运算
print(scores + 5) # 每科都加5分
# 过滤
print(scores[scores > 80]) # 大于80分的科目
DataFrame常用操作
# 创建DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
})
# 查看数据
print(df.head()) # 前几行
print(df.info()) # 数据信息
print(df.describe()) # 统计信息
# 选择列
print(df['姓名']) # 返回Series
print(df[['姓名', '年龄']]) # 返回DataFrame
# 选择行
print(df.iloc[0]) # 第一行(按位置)
print(df.loc[0]) # 第一行(按索引)
# 条件过滤
print(df[df['年龄'] > 25]) # 年龄大于25的
# 添加新列
df['薪资'] = [8000, 12000, 15000]
# 删除列
df = df.drop('城市', axis=1)
实战案例:这才知道有多爽
案例1:处理销售数据
# 模拟销售数据
sales_data = {
'日期': ['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02'],
'产品': ['手机', '电脑', '手机', '电脑'],
'销量': [100, 50, 120, 60],
'单价': [3000, 5000, 3000, 5000]
}
df = pd.DataFrame(sales_data)
# 计算销售额
df['销售额'] = df['销量'] * df['单价']
# 按日期汇总
daily_sales = df.groupby('日期')['销售额'].sum()
print(daily_sales)
# 输出:
# 日期
# 2024-01-01 550000
# 2024-01-02 660000
# Name: 销售额, dtype: int64
案例2:数据清洗
# 脏数据
dirty_data = pd.DataFrame({
'姓名': ['张三', '李四', '', '王五', None],
'年龄': [25, '三十岁', 35, None, 28],
'工资': [8000, 12000, -100, 15000, None]
})
# 处理缺失值
df_clean = dirty_data.dropna() # 删除空值
df_clean = dirty_data.fillna({'年龄': 0, '工资': 0}) # 填充空值
# 数据类型转换
df_clean['年龄'] = pd.to_numeric(df_clean['年龄'], errors='coerce')
# 过滤异常值
df_clean = df_clean[df_clean['工资'] > 0] # 工资必须大于0
踩坑提醒:别掉这些坑
坑1:索引不一致导致计算错误
# ❌ 错误示范
s1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s2 = pd.Series([4, 5, 6], index=['a', 'c', 'd'])
result = s1 + s2
print(result)
# 输出:
# a 5.0
# b NaN # 没匹配上,变成NaN
# c 7.0
# d NaN # 没匹配上,变成NaN
# ✅ 正确做法:对齐索引或重置索引
s2 = s2.reindex(s1.index) # 对齐索引
result = s1 + s2
坑2:链式赋值不生效
# ❌ 错误示范 - 链式赋值
df[df['年龄'] > 30]['工资'] = 20000 # 这行代码可能不会生效
# ✅ 正确做法 - 使用.loc
df.loc[df['年龄'] > 30, '工资'] = 20000
坑3:复制视图vs修改原数据
# ❌ 修改了原数据
df_subset = df[df['年龄'] > 25]
df_subset['工资'] = 99999 # 这会修改原df!
# ✅ 明确复制
df_subset = df[df['年龄'] > 25].copy()
df_subset['工资'] = 99999 # 这只修改副本
性能对比:差距有多大?
用100万行数据做个简单测试:
import pandas as pd
import numpy as np
import time
# 创建大数据集
data_size = 1000000
df = pd.DataFrame({
'A': np.random.randn(data_size),
'B': np.random.randn(data_size),
'C': np.random.randn(data_size)
})
# pandas计算
start_time = time.time()
result_pandas = df['A'] + df['B'] * df['C']
pandas_time = time.time() - start_time
# 纯Python计算(仅演示,实际会很慢)
start_time = time.time()
result_python = [df['A'][i] + df['B'][i] * df['C'][i] for i in range(1000)] # 只取1000个,否则太慢
python_time = time.time() - start_time
print(f"Pandas处理100万行用时: {pandas_time:.4f}秒")
print(f"纯Python处理1000行用时: {python_time:.4f}秒")
结果?Pandas比纯Python快了几百倍!(°□°;)
什么时候用什么?选择指南
用Series的场景:
- 单一维度的数据(温度、价格、成绩等)
- 需要标签索引的序列
- 时间序列数据
- 配置参数列表
用DataFrame的场景:
- 多维度数据(表格数据)
- 需要复杂的查询和过滤
- 数据分析和统计
- 数据清洗和转换
- 机器学习的特征工程
记住:DataFrame是Series的超集,Series能做的DataFrame都能做,但Series更轻量。
写在最后
搞懂了Series和DataFrame,你就掌握了Pandas的80%。
但是等等,你可能还想问:那怎么读取Excel文件?怎么处理时间数据?怎么合并两个DataFrame?
这些坑,咱们下篇再填。
记住一句话:Series是一维带标签数组,DataFrame是二维表格结构。 抓住这个本质,其他都是细节。
下次再遇到数据分析任务,别再写for循环了,直接上Pandas,保准又快又稳。
延伸阅读:
- Pandas官方文档
- 《利用Python进行数据分析》- Wes McKinney(Pandas作者)
你在用Pandas时遇到过什么坑?评论区聊聊,看看谁踩的坑更惨。 (*/ω*)