Pandas入门:Series一维数组完全指南
本文为 Pandas 完全指南 系列第一篇,适合零基础到进阶的数据分析师。我将用10年的数据科学经验,带你从Excel思维平滑过渡到Pandas,彻底掌握Series的核心概念与实战技巧。
一、为什么要学习Pandas?
1.1 Excel vs Pandas:思维转换
| 维度 | Excel | Pandas |
|---|---|---|
| 数据处理能力 | 百万级以下 | 亿级数据 |
| 自动化程度 | 手动+公式 | 全自动脚本 |
| 可复用性 | 需要重复操作 | 代码一次编写,多次运行 |
| 学习曲线 | 直观但功能分散 | 系统性,一次学习终身受益 |
| 扩展性 | 有限 | 与Python生态无缝集成 |
1.2 Pandas在数据科学中的位置
数据科学工作流:
数据获取 → 数据清洗 → 数据分析 → 数据可视化 → 模型建立
↓ ↓ ↓ ↓ ↓
requests → Pandas → Pandas → Matplotlib → Scikit-learn
Pandas是Python数据科学生态的核心枢纽,承上启下,处理80%的数据操作。
二、环境配置:
2.1 最佳安装方案
# 方案1:新手友好(使用Anaconda)
# 下载地址:https://www.anaconda.com/products/distribution
# 包含Pandas + NumPy + Matplotlib + Jupyter
# 方案2:专业开发者
pip install pandas numpy matplotlib jupyter
# 方案3:指定版本安装(推荐)
pip install pandas==1.5.3 numpy==1.24.3
2.2 版本检查与配置优化
import pandas as pd
import numpy as np
# 显示版本信息
print(f"Pandas版本: {pd.__version__}")
print(f"NumPy版本: {np.__version__}")
# 设置显示配置(写在代码开头)
pd.set_option('display.max_rows', 100) # 最大显示行数
pd.set_option('display.max_columns', 50) # 最大显示列数
pd.set_option('display.width', 200) # 显示宽度
pd.set_option('display.float_format', '{:.2f}'.format) # 浮点数格式
pd.set_option('mode.chained_assignment', None) # 关闭链式赋值警告
2.3 Jupyter Notebook最佳实践
# 在Jupyter中显示所有输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
# 单元格魔法命令(提高代码效率)
%load_ext autoreload
%autoreload 2
%matplotlib inline
三、Series深度解析:不只是Excel的一列
3.1 Series的底层结构
# 让我们深入Series的构造
import inspect
# 查看Series类的构造函数签名
print("Series构造函数的参数:")
sig = inspect.signature(pd.Series)
for param_name, param in sig.parameters.items():
print(f" {param_name}: {param.annotation}")
Series的组成:
Series = 数据值(values) + 索引(index) + 元数据(metadata)
↓ ↓ ↓
实际数据 数据标签 数据类型、名称等
3.2 四种创建方式的底层原理
方式一:标量创建 - 广播机制
# 创建过程解析
data = 5 # 标量
index = ['A', 'B', 'C', 'D']
# 底层发生的过程:
# 1. 将标量5广播为数组 [5, 5, 5, 5]
# 2. 创建索引对象 Index(['A', 'B', 'C', 'D'])
# 3. 组合成Series
series = pd.Series(data, index=index)
print(f"内存地址是否连续: {series.values.flags['C_CONTIGUOUS']}")
print(f"数据类型: {series.dtype}")
方式二:列表创建 - 性能对比
import time
import sys
# 测试不同数据结构的创建性能
def test_creation_performance():
sizes = [1000, 10000, 100000]
for size in sizes:
# Python列表
start = time.time()
py_list = list(range(size))
py_time = time.time() - start
# NumPy数组
start = time.time()
np_array = np.arange(size)
np_time = time.time() - start
# Pandas Series
start = time.time()
pd_series = pd.Series(np_array)
pd_time = time.time() - start
print(f"大小: {size:6d} | 列表: {py_time:.6f}s | NumPy: {np_time:.6f}s | Series: {pd_time:.6f}s")
test_creation_performance()
方式三:字典创建 - 索引对齐机制
# 字典创建的索引对齐机制
data_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}
# 情况1:完全匹配
series1 = pd.Series(data_dict)
print("情况1 - 完全匹配:")
print(series1)
# 情况2:指定额外索引
series2 = pd.Series(data_dict, index=['A', 'B', 'E', 'F'])
print("\n情况2 - 指定额外索引:")
print(series2)
print(f"NaN值数量: {series2.isna().sum()}")
# 情况3:指定部分索引
series3 = pd.Series(data_dict, index=['A', 'C'])
print("\n情况3 - 指定部分索引:")
print(series3)
方式四:NumPy数组创建 - 内存共享
# NumPy数组与Series的内存关系
original_array = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
# 创建Series(默认情况下共享内存)
series_shared = pd.Series(original_array)
print(f"原始数组id: {id(original_array)}")
print(f"Series底层数组id: {id(series_shared.values)}")
print(f"内存是否共享: {np.shares_memory(original_array, series_shared.values)}")
# 修改原始数组会影响Series
original_array[0] = 999
print(f"修改后Series: {series_shared.values[0]}")
# 创建不共享内存的Series
series_copied = pd.Series(original_array, copy=True)
original_array[1] = 888
print(f"拷贝后修改不影响: {series_copied.values[1]}")
3.3 创建方式的性能与适用场景矩阵
| 创建方式 | 内存效率 | 速度 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| 标量 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 初始化相同值 | 适合小数据量 |
| 列表 | ⭐⭐⭐ | ⭐⭐⭐ | 通用场景 | 中等数据量 |
| 字典 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 键值对数据 | 自动索引对齐 |
| NumPy数组 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 大规模数值计算 | 注意内存共享 |
四、Series索引:不只是行号
4.1 默认索引 vs 自定义索引
# 默认整数索引
series_default = pd.Series([10, 20, 30, 40, 50])
print("默认索引:")
print(series_default.index) # RangeIndex(start=0, stop=5, step=1)
# 自定义索引(字符串)
series_custom = pd.Series([10, 20, 30, 40, 50],
index=['A', 'B', 'C', 'D', 'E'])
print("\n自定义索引:")
print(series_custom.index) # Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
# 自定义索引(混合类型)
series_mixed = pd.Series([10, 20, 30, 40, 50],
index=[1, 'B', 3.0, 'D', True])
print("\n混合类型索引:")
print(series_mixed.index)
4.2 索引的类型系统
# 查看索引的详细属性
def analyze_index(series):
print(f"索引类型: {type(series.index)}")
print(f"索引值: {series.index}")
print(f"索引数据类型: {series.index.dtype}")
print(f"是否唯一: {series.index.is_unique}")
print(f"是否单调递增: {series.index.is_monotonic_increasing}")
print("-" * 40)
# 测试不同类型的索引
analyze_index(pd.Series(range(5))) # 默认整数索引
analyze_index(pd.Series(range(5), index=['A','B','C','D','E'])) # 字符串索引
analyze_index(pd.Series(range(5), index=pd.date_range('2024-01-01', periods=5))) # 日期索引
4.3 索引的性能影响
import timeit
def test_index_performance():
# 创建测试数据
size = 100000
data = np.random.randn(size)
# 整数索引
int_index = pd.Series(data, index=range(size))
# 字符串索引
str_index = pd.Series(data, index=[f'row_{i}' for i in range(size)])
# 测试访问性能
int_time = timeit.timeit(lambda: int_index[50000], number=10000)
str_time = timeit.timeit(lambda: str_index['row_50000'], number=10000)
print(f"整数索引访问时间: {int_time:.6f}s")
print(f"字符串索引访问时间: {str_time:.6f}s")
print(f"性能差异: {(str_time/int_time - 1)*100:.2f}%")
test_index_performance()
五、Series数据类型:选择合适的dtype
5.1 常见数据类型及其内存占用
# 查看不同数据类型的内存占用
def show_memory_usage():
data_types = {
'int8': np.int8,
'int32': np.int32,
'int64': np.int64,
'float32': np.float32,
'float64': np.float64,
'object': object
}
for dtype_name, dtype in data_types.items():
# 创建Series
if dtype == object:
series = pd.Series(['a'] * 1000, dtype=dtype)
else:
series = pd.Series(np.ones(1000, dtype=dtype))
# 计算内存占用
memory_bytes = series.memory_usage(deep=True)
print(f"{dtype_name:10s}: {memory_bytes:8d} bytes")
show_memory_usage()
5.2 创建时指定数据类型
# 自动推断 vs 手动指定
data = [1, 2, 3, 4, 5]
# 自动推断(通常是int64)
series_auto = pd.Series(data)
print(f"自动推断类型: {series_auto.dtype}")
# 手动指定类型
series_int8 = pd.Series(data, dtype='int8')
series_float32 = pd.Series(data, dtype='float32')
series_category = pd.Series(['A', 'B', 'C', 'A', 'B'], dtype='category')
print(f"指定int8类型: {series_int8.dtype}, 内存: {series_int8.memory_usage()} bytes")
print(f"指定float32类型: {series_float32.dtype}, 内存: {series_float32.memory_usage()} bytes")
print(f"指定category类型: {series_category.dtype}, 内存: {series_category.memory_usage()} bytes")
5.3 数据类型转换的最佳实践
# 场景:从CSV读取的数据通常是object类型,需要转换
def optimize_data_types(series):
"""自动优化Series的数据类型"""
original_dtype = series.dtype
original_memory = series.memory_usage(deep=True)
if series.dtype == 'object':
# 尝试转换为数值类型
try:
series = pd.to_numeric(series, errors='coerce')
except:
pass
# 如果是字符串且重复值多,转换为分类
if series.dtype == 'object' and series.nunique() / len(series) < 0.5:
series = series.astype('category')
# 对于整数,尝试向下转换
if pd.api.types.is_integer_dtype(series.dtype):
min_val, max_val = series.min(), series.max()
for dtype in [np.int8, np.int16, np.int32]:
if min_val >= np.iinfo(dtype).min and max_val <= np.iinfo(dtype).max:
series = series.astype(dtype)
break
# 对于浮点数,尝试转换为float32
if pd.api.types.is_float_dtype(series.dtype):
series = series.astype(np.float32)
new_memory = series.memory_usage(deep=True)
reduction = (1 - new_memory/original_memory) * 100
print(f"原始类型: {original_dtype}, 内存: {original_memory:,} bytes")
print(f"优化后类型: {series.dtype}, 内存: {new_memory:,} bytes")
print(f"内存减少: {reduction:.1f}%")
return series
# 测试优化
test_data = pd.Series(['1', '2', '3', '4', '5'] * 10000) # 重复的字符串数字
optimized = optimize_data_types(test_data)
记得收藏本文,这是你Pandas学习之路的第一站。下一篇我们将深入讲解DataFrame,带你从一维世界进入二维表格的精彩世界!
关键词:Pandas入门、Series详解、Python数据分析、数据科学基础
1076

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



