|

Pandas Series API 手册

Pandas Series API 完整手册

1. Series 创建

1.1 基本创建方法

import pandas as pd
import numpy as np

# 从列表/数组创建
s1 = pd.Series([1, 3, 5, np.nan, 6, 8])
s2 = pd.Series(np.arange(10))

# 从字典创建
s3 = pd.Series({'a': 1, 'b': 2, 'c': 3})

# 指定索引
s4 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s5 = pd.Series([10, 20, 30], index=pd.date_range('2023-01-01', periods=3))

# 从标量创建
s6 = pd.Series(5, index=['a', 'b', 'c'])  # 全为5
s7 = pd.Series(5.0, index=range(5))

# 空Series
s_empty = pd.Series(dtype='float64')

print("Series示例:")
print(s1)
print("\n索引:", s1.index)
print("值:", s1.values)
print("类型:", s1.dtype)

1.2 高级创建

# 指定数据类型
s_int = pd.Series([1, 2, 3], dtype='int32')
s_float = pd.Series([1.0, 2.0, 3.0], dtype='float32')
s_category = pd.Series(['A', 'B', 'A'], dtype='category')

# 带名称的Series
s_named = pd.Series([1, 2, 3], name='values', index=['x', 'y', 'z'])

# 从其他Series复制
s_copy = s1.copy(deep=True)

# 使用pd.array创建(Pandas 0.24+)
s_arrow = pd.Series(pd.array([1, 2, None], dtype='Int64'))  # 可空整数

2. 属性和方法

2.1 基本属性

s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])

# 基本信息
print("长度:", len(s))
print("形状:", s.shape)
print("索引:", s.index)
print("值:", s.values)
print("数据类型:", s.dtype)
print("名称:", s.name)
print("是否为空:", s.empty)
print("轴:", s.axes)

# 内存使用
print("内存使用:", s.memory_usage(index=True, deep=True))

# 数据类型信息
print("可空类型:", s.dtype.name)
print("是否为可空:", s.dtype.is_nullable)

2.2 索引属性

# 索引操作
print("索引类型:", type(s.index))
print("索引值:", s.index.tolist())
print("是否唯一:", s.index.is_unique)
print("是否单调递增:", s.index.is_monotonic_increasing)

# 设置名称
s.index.name = 'labels'
print("索引名称:", s.index.name)

3. 索引和选择

3.1 位置索引(iloc)

s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])

# 单个元素
print("位置索引0:", s.iloc[0])      # 10
print("位置索引-1:", s.iloc[-1])     # 50

# 切片
print("位置切片0:2:", s.iloc[0:2])   # a, b
print("位置切片1:", s.iloc[1:])      # b, c, d, e

# 列表索引
print("位置列表[0,2,4]:", s.iloc[[0, 2, 4]])  # a, c, e

# 布尔索引
mask = s.iloc > 25
print("布尔选择:", s.iloc[mask])

3.2 标签索引(loc)

# 单个标签
print("标签'a':", s.loc['a'])       # 10

# 标签切片
print("标签切片a:c:", s.loc['a':'c'])  # a, b, c

# 标签列表
print("标签列表['a','c','e']:", s.loc[['a', 'c', 'e']])

# 条件索引
print("条件选择 (值>25):", s.loc[s > 25])

# 多条件
print("多条件:", s.loc[(s > 20) & (s.index != 'b')])

3.3 混合索引(at/iat)

# 快速单元素访问(更快但只支持单个元素)
print("at标签'a':", s.at['a'])      # 10
print("iat位置0:", s.iat[0])         # 10

# 警告:不支持切片
# s.at['a':'c']  # TypeError

3.4 高级索引

# 索引不存在时的行为
try:
    s.loc['missing']
except KeyError as e:
    print("KeyError:", e)

# 使用get方法(安全)
print("get('missing', default=-1):", s.get('missing', default=-1))

# 部分匹配(模糊索引)
print("部分匹配:", s.loc[s.index.str.startswith('a')])

# 多级索引(如果索引是MultiIndex)
index = pd.MultiIndex.from_product([['A', 'B'], [1, 2]])
s_multi = pd.Series(np.arange(4), index=index)
print("多级索引:", s_multi.loc['A'])  # 选择第一级

4. 数据操作

4.1 赋值和修改

s = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])

# 直接赋值
s['a'] = 10
s.iloc[1] = 20

# 批量赋值
s.loc[['b', 'c']] = [200, 300]
s.iloc[0:2] = [100, 200]

# 条件赋值
s.loc[s > 200] = 0

# 使用update方法
s2 = pd.Series([999, 888], index=['a', 'b'])
s.update(s2)
print("更新后:", s)

4.2 插入和删除

# 插入
s = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s['d'] = 4
s2 = s.append(pd.Series([5], index=['e']))  # 旧方法,已弃用
s_new = pd.concat([s, pd.Series([5], index=['e'])])  # 新方法

# 删除
s_dropped = s.drop('c')
s_dropped2 = s.drop(['a', 'b'])

# 使用del
del s['a']
print("删除后:", s)

4.3 排序

s = pd.Series([3, 1, 4, 1, 5], index=['e', 'a', 'b', 'c', 'd'])

# 按值排序
s_sorted = s.sort_values()
print("按值排序:", s_sorted)

# 按索引排序
s_idx_sorted = s.sort_index()
print("按索引排序:", s_idx_sorted)

# 带参数排序
s_sorted_asc = s.sort_values(ascending=False, na_position='first')
s_stable = s.sort_values(ascending=True, kind='stable')  # 稳定排序

# 忽略索引排序
s_values_only = s.sort_values().reset_index(drop=True)

5. 缺失值处理

5.1 检测缺失值

s = pd.Series([1, np.nan, 3, None, 5])

# 检测
print("是否有NaN:", s.hasnans)
print("NaN位置:", s.isna())
print("非NaN位置:", s.notna())
print("NaN数量:", s.isna().sum())

# 特定值检查
print("是否全为NaN:", s.isna().all())
print("是否部分NaN:", s.isna().any())

5.2 填充缺失值

# 常用填充方法
s_filled = s.fillna(0)                    # 用0填充
s_forward = s.fillna(method='ffill')      # 前向填充
s_backward = s.fillna(method='bfill')     # 后向填充

# 指定值填充
s_mean = s.fillna(s.mean())
s_median = s.fillna(s.median())

# 插值填充
s_interp = s.interpolate(method='linear')
s_time = pd.Series([1, np.nan, np.nan, 4], 
                   index=pd.date_range('2023-01-01', periods=4))
s_time_interp = s_time.interpolate(method='time')

# 组合填充
s_combined = s.fillna(method='ffill').fillna(0)

print("填充结果:", s_filled)

5.3 删除缺失值

# 删除含NaN的元素
s_clean = s.dropna()

# 按阈值删除(至少保留n个非NaN值)
s_threshold = s.dropna(thresh=2)

print("删除NaN后:", s_clean)

6. 统计方法

6.1 描述性统计

s = pd.Series(np.random.randn(1000))

print("描述性统计:")
print(s.describe())

# 单独统计量
print("均值:", s.mean())
print("中位数:", s.median())
print("标准差:", s.std())
print("方差:", s.var())
print("最小值:", s.min())
print("最大值:", s.max())
print("四分位数:", s.quantile([0.25, 0.5, 0.75]))

6.2 聚合函数

# 基本聚合
print("总和:", s.sum())
print("计数(非NaN):", s.count())
print("累积和:", s.cumsum())
print("累积最大:", s.cummax())
print("累积最小:", s.cummin())

# 移动统计
print("滚动均值:", s.rolling(5).mean().head())
print("扩展统计:", s.expanding().mean().head())

# 分位数
print("95%分位:", s.quantile(0.95))

6.3 相关性

s1 = pd.Series(np.random.randn(100))
s2 = pd.Series(np.random.randn(100))

# 皮尔逊相关系数
corr = s1.corr(s2)
print("相关系数:", corr)

# 协方差
cov = s1.cov(s2)
print("协方差:", cov)

# 自动相关
autocorr = s1.autocorr(lag=1)
print("自相关:", autocorr)

7. 字符串方法(str访问器)

7.1 基本字符串操作

s_str = pd.Series(['Apple', 'Banana', 'Cherry', None, 'Date'])

# 长度
print("字符串长度:", s_str.str.len())

# 大小写
print("大写:", s_str.str.upper())
print("小写:", s_str.str.lower())
print("标题:", s_str.str.title())

# 查找和替换
print("包含'A':", s_str.str.contains('A'))
print("替换:", s_str.str.replace('a', 'A'))

# 分割和提取
print("首字母:", s_str.str[0])
print("分割:", s_str.str.split(''))

7.2 正则表达式

email_s = pd.Series(['user@example.com', 'test@domain.org', None])

# 正则匹配
print("是邮箱:", email_s.str.match(r'^[\w\.-]+@[\w\.-]+\.\w+$'))
print("提取域名:", email_s.str.extract(r'@([\w\.-]+)'))

# 查找所有匹配
print("所有匹配:", email_s.str.findall(r'\w+'))

# 替换正则
print("替换数字:", s_str.str.replace(r'\d+', '', regex=True))

7.3 高级字符串方法

# 去除空格
print("去除空格:", s_str.str.strip())

# 填充
print("左填充:", s_str.str.pad(10, side='left', fillchar=' '))
print("居中:", s_str.str.center(10, fillchar='-'))

# 翻译
trans_map = str.maketrans('ae', 'AE')
print("翻译:", s_str.str.translate(trans_map))

# 格式化
print("格式化:", s_str.str.zfill(10))  # 数字字符串补0

8. 数值方法(数值Series)

8.1 数学运算

s_num = pd.Series([1, 2, 3, 4])

# 基本运算
print("加法:", s_num + 10)
print("乘法:", s_num * 2)
print("幂运算:", s_num ** 2)

# 应用函数
print("平方根:", np.sqrt(s_num))
print("对数:", np.log(s_num))
print("指数:", np.exp(s_num - 1))

# 自定义函数
print("自定义:", s_num.apply(lambda x: x**2 + 1))

8.2 统计函数

# 排名
print("排名:", s_num.rank(method='average'))

# 差分
print("一阶差分:", s_num.diff())
print("二阶差分:", s_num.diff(2))

# 百分比变化
print("百分比变化:", s_num.pct_change())

# 移动窗口
print("移动最大:", s_num.rolling(2).max())

8.3 数值转换

# 舍入
print("四舍五入:", s_num.round(1))
print("向下取整:", np.floor(s_num))
print("向上取整:", np.ceil(s_num))

# 裁剪
print("裁剪到[1, 3]:", s_num.clip(lower=1, upper=3))

# 标准化
print("Z-score:", (s_num - s_num.mean()) / s_num.std())

9. 时间序列功能

9.1 创建时间序列

# 日期范围索引
dates = pd.date_range('2023-01-01', periods=5, freq='D')
ts = pd.Series(np.random.randn(5), index=dates)

# 从字符串解析
ts_str = pd.to_datetime(['2023-01-01', '2023-01-02', '2023-01-03'])
ts2 = pd.Series([1, 2, 3], index=ts_str)

print("时间序列:", ts)

9.2 时间属性

# 日期时间属性
print("年份:", ts.index.year)
print("月份:", ts.index.month)
print("星期几:", ts.index.day_name())
print("是否周末:", ts.index.dayofweek.isin([5, 6]))

# 时间段
print("季度:", ts.index.quarter)
print("是否闰年:", ts.index.is_leap_year)

9.3 时间操作

# 移位
print("向前移位:", ts.shift(1))
print("向后移位:", ts.shift(-1))

# 重采样
monthly = ts.resample('M').mean()
print("月度重采样:", monthly)

# 滚动窗口
rolling = ts.rolling('3D').mean()
print("3天滚动均值:", rolling)

10. 分类数据

10.1 创建分类Series

# 分类Series
cat_s = pd.Series(['A', 'B', 'A', 'C', 'B'], 
                  dtype=pd.CategoricalDtype(categories=['A', 'B', 'C'], ordered=True))

print("分类Series:", cat_s)
print("分类:", cat_s.dtype.categories)
print("有序:", cat_s.dtype.ordered)
print("编码:", cat_s.cat.codes)

10.2 分类操作

# 添加/删除分类
cat_s = cat_s.cat.add_categories('D')
print("添加分类D:", cat_s.cat.categories)

# 移除未使用分类
cat_s = cat_s.cat.remove_unused_categories()
print("移除未用分类:", cat_s.cat.categories)

# 重命名分类
cat_s = cat_s.cat.rename_categories({'A': 'Excellent', 'B': 'Good', 'C': 'Poor'})

# 排序(基于分类顺序)
print("分类排序:", cat_s.sort_values())

11. 高级功能

11.1 映射和转换

# map方法
def custom_map(x):
    return x * 2 if x > 0 else 0

s_mapped = s.map(custom_map)
print("映射结果:", s_mapped)

# apply方法(更灵活)
s_applied = s.apply(lambda x: x**2)
print("apply结果:", s_applied)

# replace方法
s_replaced = s.replace({1: 100, 2: 200})
print("替换结果:", s_replaced)

11.2 组合操作

s1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s2 = pd.Series([4, 5, 6], index=['b', 'c', 'd'])

# 连接
s_concat = pd.concat([s1, s2])
print("连接:", s_concat)

# 合并(类似SQL join)
s_merge = s1.combine_first(s2)
print("合并:", s_merge)

# 按索引对齐运算
s_aligned = s1 + s2
print("对齐运算:", s_aligned)

11.3 窗口函数

# 滚动窗口
rolling_mean = s.rolling(window=3).mean()
rolling_sum = s.rolling(window=3).sum()
rolling_std = s.rolling(window=3).std()

# 自定义窗口函数
def custom_window(x):
    return x.max() - x.min()

rolling_range = s.rolling(3).apply(custom_window)

# 指数加权
ewm_mean = s.ewm(span=3).mean()

# 扩展窗口
expanding_mean = s.expanding().mean()

12. 输入输出

12.1 Series IO

s = pd.Series(np.random.randn(100))

# CSV
s.to_csv('series.csv')
s_csv = pd.read_csv('series.csv', index_col=0, squeeze=True)

# JSON
s.to_json('series.json')
s_json = pd.read_json('series.json', typ='series')

# Pickle
s.to_pickle('series.pkl')
s_pkl = pd.read_pickle('series.pkl')

# 剪贴板
s.to_clipboard()
s_clip = pd.read_clipboard(index_col=0, squeeze=True)

12.2 数据库操作

# 写入数据库
s.to_sql('series_table', engine, if_exists='replace')

# 从数据库读取
s_db = pd.read_sql('SELECT * FROM series_table', engine, index_col='index').squeeze()

13. 最佳实践和性能优化

13.1 性能优化

# 1. 使用向量化操作而非apply
s_vec = s * 2  # 向量化,快速
# s.apply(lambda x: x * 2)  # 慢

# 2. 选择合适的数据类型
s_int8 = s.astype('int8') if s.dtype.kind == 'i' else s
s_category = s.astype('category') if s.dtype == 'O' and s.nunique() < len(s) * 0.5 else s

# 3. 使用get而不是直接索引
value = s.get('key', default=np.nan)  # 安全

# 4. 批量操作
mask = s > s.median()
s.loc[mask] = 0  # 批量赋值

13.2 内存优化

def optimize_series_memory(s):
    """优化Series内存使用"""
    optimized = s.copy()

    # 数值类型优化
    if optimized.dtype.kind in 'if':
        optimized = pd.to_numeric(optimized, downcast='integer' if 'i' in optimized.dtype.kind else 'float')

    # 字符串→分类
    if optimized.dtype == 'O':
        nunique = optimized.nunique()
        if nunique / len(optimized) < 0.5:
            optimized = optimized.astype('category')

    # 布尔优化
    if set(optimized.dropna().unique()) <= {True, False}:
        optimized = optimized.astype(bool)

    return optimized

s_opt = optimize_series_memory(s)
print(f"优化前内存: {s.memory_usage(deep=True)}")
print(f"优化后内存: {s_opt.memory_usage(deep=True)}")

13.3 错误处理

def safe_series_operation(s, operation):
    """安全的Series操作"""
    try:
        if operation == 'mean':
            return s.mean()
        elif operation == 'sum':
            return s.sum()
        # 添加更多操作
    except Exception as e:
        print(f"操作失败: {e}")
        return np.nan

# 使用
result = safe_series_operation(s, 'mean')

Pandas Series 是处理一维数据的核心数据结构,提供了丰富的索引、统计、时间序列和字符串处理功能。通过掌握这些API,可以高效处理各种数据分析任务。关键是选择合适的索引方法、优化数据类型,并优先使用向量化操作以获得最佳性能。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注